function is not a function - react-native - react-native

In my project, I used stack navigator as the navigator. Inside navigationOptions, at headerRight, I used a custom button. When I try to call the onPress, it says the function is not a function.
this is the function
sharePost() {
// this.props.doShare();
console.log("DONE");
}
I have put the full code here. I want to use sharePost function inside the rightHeader of navigationOptions.
import React, { Component } from "react";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import {
View,
Text,
Image,
TouchableOpacity,
Animated,
ScrollView,
StyleSheet,
Dimensions
} from "react-native";
import { PostProfileBar, WritePost } from "../../components";
import { ButtonWithoutBackground } from "../../mixing/UI";
const width = Dimensions.get("window").width;
const height = Dimensions.get("window").height / 3;
class SharePostScreen extends Component {
constructor(props) {
super(props);
sharePost() {
// this.props.doShare();
console.log("DONE");
}
render() {
return (
<View style={styles.container}>
<ScrollView>
<WritePost profile={this.state.loggedUserProfile} />
<View style={styles.sharePostWrapper}>
<PostProfileBar profile={this.state.postedUserProfile} />
<Image
source={{
uri: "https://pbs.twimg.com/media/DWvRLbBVoAA4CCM.jpg"
}}
resizeMode={"stretch"}
style={styles.image}
/>
</View>
</ScrollView>
</View>
);
}
static navigationOptions = ({ navigation }) => ({
headerTitle: "Share To Feed",
headerTitleStyle: {
paddingLeft: "20%",
paddingRight: "20%"
},
headerStyle: {
paddingRight: 10,
paddingLeft: 10
},
headerLeft: (
<Icon
name={"close"}
size={30}
onPress={() => {
navigation.goBack();
}}
/>
),
headerRight: (
<ButtonWithoutBackground
buttonText={styles.buttonText}
onPress={this.sharePost()}
>
Post
</ButtonWithoutBackground>
)
});
}

In order to call the class level methods inside the static method navigationOptions, you can do the following,
// Bind function using setParams
componentDidMount() {
const { navigation } = this.props;
navigation.setParams({
referencedSharePost: this.sharePost,
});
}
sharePost = (parameters) => {
// this.props.doShare();
console.log("DONE" + parameters);
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
//... Other Options
headerRight: (
<ButtonWithoutBackground
buttonText={styles.buttonText}
onPress={() => params.referencedSharePost("I'm passing something")}
>
Post
</ButtonWithoutBackground>
)
}
}

Related

How to separate API call using react natives context API?

I am new to react native. I have following component in my project for now I have written for fetching API in same component but want to separate it out. I am getting difficulty for how can i access variable which I am using in "getAlbum" method from outside of component.
I am trying to do this using new concept context API . is this possible using context API and how?
Is there standard way to separate API call from component?
import React, { Component } from 'react';
import {
FlatList, Text, View, Image, TouchableOpacity,
} from 'react-native';
import { ActivityIndicator, Provider } from 'react-native-paper';
import axios from 'axios';
import styles from '../style/ThumbnailView.component.style';
import ErrorAlert from '../common/ErrorAlert';
import * as myConstant from '../common/Constants';
export default class HomeScreen extends Component {
// For to Navigation header
static navigationOptions = () => ({
headerTitle: 'Album Information',
});
constructor(props) {
super(props);
this.state = {
isLoading: true,
apiLoadingError: false,
};
}
getAlbums() {
const { navigation } = this.props;
const albumId = navigation.getParam('albumID', 'no data');
axios
.get(
myConstant.API + `photos?albumId=${albumId}`, {timeout: myConstant.TIMEOUT}
)
.then((response) => {
this.setState({
isLoading: false,
dataSource: response.data,
});
})
.catch(err => {
this.setState({isLoading: false, apiLoadingError: true})
});
}
componentDidMount() {
this.getAlbums();
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 30 }}>
<ActivityIndicator animating={true} size='large' />
</View>
);
}
if (this.state.apiLoadingError) {
return (
<ErrorAlert />
);
}
return (
<React.Fragment>
<Provider>
<View style={styles.listContainer} >
<FlatList
testID='flatlist'
data={ this.state.dataSource } numColumns={3}
renderItem={({ item }) => <View style={styles.listRowContainer}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('AlbumDetailsViewScreen', {
albumTitle: item.title, albumImg: item.url
})} style={styles.listRow}>
<View style={styles.listTextNavVIew}>
<Image source = {{ uri: item.thumbnailUrl }} style={styles.imageViewContainer} />
</View>
</TouchableOpacity>
</View>
}
keyExtractor = { (item, index) => index.toString() }
/>
</View>
</Provider>
</React.Fragment>
);
}
}

How to avoid data duplicate when navigate screen?

My situation is i have two FlatList component (A and B).
A is my first screen. I use react-navigation to navigate A to B , B will show back arrow on headerLeft. When i click the arrow it will back to A . But the FlatList data is still show B even it is really in A...
My data is from fetch API by react-redux, i think the problem is come from react-redux. Because i test a simple test without react-redux. The problem is gone.
I want to use react-redux create my project. I try to use shouldComponentUpdate like
shouldComponentUpdate = (nextProps, nextState) => {
if (nextProps.movieList === this.props.movieList) {
return false;
}
return true;
};
It is still can't fix my problem when goBack() to another component
I console.log it try to find what is going on with my props data.
When i navigate to B from A. My console.log will show like this, i find A component will be rendered...
Then i click the back arrow on headerLeft to A. The screen is A but the data is still B add my console.log is empty at the same time.
I can't figure it out. Any help would be appreciated. Thanks in advance.
Here is my A component file (B is similar with A):
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
TouchableOpacity, Image,
ActivityIndicator, Alert, Platform
} from 'react-native';
import { Icon } from 'react-native-elements';
import { connect } from 'react-redux';
import { fetchMainMovieList } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class MainActivity extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'MainActivity',
headerLeft:
<TouchableOpacity style={{ marginLeft: 10 }} onPress={() => navigation.navigate('DrawerOpen')} >
<Icon name='menu' />
</TouchableOpacity>
});
componentWillMount() {
this.props.fetchMainMovieList();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 220, width: equalWidth }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('A component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={2}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchMainMovieList })(MainActivity);
Here is my B component file:
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
Image, ActivityIndicator, Text
} from 'react-native';
import { connect } from 'react-redux';
import { fetchThisWeek } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class ThisWeek extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'ThisWeek',
});
componentWillMount() {
this.props.fetchThisWeek();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 500, width: '100%' }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('B component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={1}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchThisWeek })(ThisWeek);
Here is my MyListReducer.js:
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return action.payload;
case MOVIELIST_THISWEEK:
return action.payload;
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return state;
}
};
In your reducer you have added the fetched data into the main object in store, instead, you should have to maintain two different variables to save data of those different components separately. Try by changing the reducer as,
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {
weeklyMovies:[],
allMovies:[]
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return {
...state,
allMovies:action.payload
};
case MOVIELIST_THISWEEK:
return {
...state,
weeklyMovies:action.payload
};
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return {...state};
}
};
And in your component A and B you should change your mapStateToProps to read data from corresponding objects in store.
For MainActivity component
const mapStateToProps = (state) => {
const movieList = state.allMovies;
return { movieList };
};
and for ThisWeek component
const mapStateToProps = (state) => {
const movieList = state.weeklyMovies;
return { movieList };
};

react native stack navigation undefined is not an object (evalutating 'props.navigation')

‌‌I'm making an app in react-native and I would like to navigate to different page by clicking on a button using stack navigation:
Here is my code :
app.js
import React from 'react';
import { AppRegistry } from 'react-native';
import { StackNavigator } from 'react-navigation';
import Home from './Screens/Home';
import VideoListItems from './Screens/VideoListItems';
import TrackPlayer from './Screens/TrackPlayer';
const reactNavigationSample = props => {
return <VideoListItems navigation={props.navigation} />;
};
reactNavigationSample.navigationOptions = {
title: "VideoListItems"
};
const AppNavigator = StackNavigator({
Home: { screen: Home, navigationOptions: { header: null }},
VideoListItems: { screen: VideoListItems, navigationOptions: { header: null }},
TrackPlayer: { screen: TrackPlayer, navigationOptions: { header: null }},
}
);
export default class App extends React.Component {
render() {
return (
<AppNavigator />
);
}
}
VideoListItems where the button to navigate is :
import {StackNavigator} from 'react-navigation';
const VideoListItems = ({ video, props }) => {
const { navigate } = props.navigation;
const {
cardStyle,
imageStyle,
contentStyle,
titleStyle,
channelTitleStyle,
descriptionStyle
} = styles;
const {
title,
channelTitle,
description,
thumbnails: { medium: { url } }
} = video.snippet;
const videoId = video.id.videoId;
return(
<View>
<Card title={null} containerStyle={cardStyle}>
<Image
style={imageStyle}
source= {{ uri: url}}
/>
<View style={contentStyle}>
<Text style={titleStyle}>
{title}
</Text>
<Text style={channelTitleStyle}>
{channelTitle}
</Text>
<Text style={descriptionStyle}>
{description}
</Text>
<Button
raised
title="Save And Play"
icon={{ name: 'play-arrow' }}
containerViewStyle={{ marginTop: 10 }}
backgroundColor="#E62117"
onPress={() => {
navigate('TrackPlayer')
}}
/>
</View>
</Card>
</View>
);
};
export default VideoListItems;
But I'm getting this error :
TypeError: undefined is not an object (evaluating 'props.navigation')
I don't know how to pass the props navigation and make it able to navigate when clicking on the button, I don't know where is my error, any ideas ?
[EDIT]
My new VideoItemList :
const VideoListItems = props => {
const {
cardStyle,
imageStyle,
contentStyle,
titleStyle,
channelTitleStyle,
descriptionStyle
} = styles;
const {
title,
channelTitle,
description,
thumbnails: { medium: { url } }
} = props.video.snippet;
const videoId = props.video.id.videoId;
const { navigate } = props.navigation;
return(
<View>
<Card title={null} containerStyle={cardStyle}>
<Image
style={imageStyle}
source= {{ uri: url}}
/>
<View style={contentStyle}>
<Text style={titleStyle}>
{title}
</Text>
<Text style={channelTitleStyle}>
{channelTitle}
</Text>
<Text style={descriptionStyle}>
{description}
</Text>
<Button
raised
title="Save And Play"
icon={{ name: 'play-arrow' }}
containerViewStyle={{ marginTop: 10 }}
backgroundColor="#E62117"
onPress={() => {
navigate.navigate('TrackPlayer')
}}
/>
</View>
</Card>
</View>
);
};
This is the file where I display all my components :
import React, { Component } from 'react';
import { View } from 'react-native';
import YTSearch from 'youtube-api-search';
import AppHeader from './AppHeader';
import SearchBar from './SearchBar';
import VideoList from './VideoList';
const API_KEY = 'ApiKey';
export default class Home extends Component {
state = {
loading: false,
videos: []
}
componentWillMount(){
this.searchYT('');
}
onPressSearch = term => {
this.searchYT(term);
}
searchYT = term => {
this.setState({ loading: true });
YTSearch({key: API_KEY, term }, videos => {
console.log(videos);
this.setState({
loading: false,
videos
});
});
}
render() {
const { loading, videos } = this.state;
return (
<View style={{ flex: 1, backgroundColor: '#ddd' }}>
<AppHeader headerText="Project Master Sound Control" />
<SearchBar
loading={loading}
onPressSearch={this.onPressSearch} />
<VideoList videos={videos} />
</View>
);
}
}
And my VideoList where I use VideoListItems :
import React from 'react';
import { ScrollView, View } from 'react-native';
import VideoListItems from './VideoListItems';
const VideoList = ({ videos }) => {
const videoItems = videos.map(video =>(
<VideoListItems
key={video.etag}
video={video}
/>
));
return(
<ScrollView>
<View style={styles.containerStyle}>
{videoItems}
</View>
</ScrollView>
);
};
const styles = {
containerStyle: {
marginBottom: 10,
marginLeft: 10,
marginRight: 10
}
}
export default VideoList;
That's because you try to extract navigation from a prop named props (that doesn't exist), you have many ways to solve this problem :
Use the rest operator to group all props except video inside a props variable
const VideoListItems = ({ video, ...props }) => {
Don't destructure you props object
const VideoListItems = props => {
// don't forget to refactor this line
const videoId = props.video.id.videoId;
Extract navigation from props
const VideoListItems = ({ video, navigation }) => {
const { navigate } = navigation;

How to call Screen / Component class method from react-navigation Header

I need to call SearchScreen class method from a React Navigation Header.
The Navigator look like this:
Search: {
screen: SearchScreen,
path: 'search/:query',
navigationOptions: {
title: 'Search',
header: {
right: (
<MaterialCommunityIcons
name="filter"
onPress={() => {
console.log(this);
}}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
},
}
}
I've made it work by doing:
// declare static navigationOptions in the Component
static navigationOptions = {
title: 'Title',
header: ({ state }) => ({
right: (
<MaterialCommunityIcons
name="filter"
onPress={state.params.handleFilterPress}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
}),
}
_handleFilterPress() {
// do something
}
componentDidMount() {
// set handler method with setParams
this.props.navigation.setParams({
handleFilterPress: this._handleFilterPress.bind(this)
});
}
I've resolved the issue the following way:
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (
<TouchableOpacity
onPress={navigation.getParam('onPressSyncButton')}>
<Text>Sync</Text>
</TouchableOpacity>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ onPressSyncButton: this._onPressSyncButton });
}
_onPressSyncButton = () => {
console.log("function called");
}
Hooks solution with FunctionComponent, useState and useEffect
Ref the official docs (https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component) it is done by:
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: <LogoTitle />,
headerRight: (
<Button
onPress={navigation.getParam('increaseCount')}
title="+1"
color="#fff"
/>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ increaseCount: this._increaseCount });
}
state = {
count: 0,
};
_increaseCount = () => {
this.setState({ count: this.state.count + 1 });
};
/* later in the render function we display the count */
}
However I could not get this to work when working with the hooks api. My state variables were always undefined, but after I thought about how the hooks api is implemented it all made sense, so the solution was to update the navigation param every time a significant state variable was changed:
const [count, setCount] = useState(0);
useEffect(() => {
props.navigation.setParams({ increaseCount });
}, [count]);
const increaseCount = () => setCount(count + 1);
I came across same issue and able to resolve the issue from below links.
class MyScreen extends React.Component {
static navigationOptions = {
header: {
right: <Button title={"Save"} onPress={() => this.saveDetails()} />
}
};
saveDetails() {
alert('Save Details');
}
render() {
return (
<View />
);
}
}
Source: react-native issues 145
Below is my code
import React, { Component } from "react";
import {
Container,
Header,
Item,
Input,
Icon,
Button,
Text,
Left,
Body,
Right,
Content,
Spinner,
List,
ListItem
} from "native-base";
import { View, Image, StyleSheet, Keyboard } from "react-native";
import { connect } from "react-redux";
import {
onClear,
onSearchTextChanged,
searchForProfiles
} from "../../actions/searchActions";
class SearchBar extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Header searchBar rounded>
<Button
iconLeft
style={{ paddingLeft: 0 }}
light
onPress={this.props.onBackPress}
>
<Icon style={{ marginLeft: 0, fontSize: 35 }} name="arrow-back" />
</Button>
<Item>
<Icon name="ios-search" />
<Input
placeholder="Search"
onChangeText={this.props.onChangeText}
value={this.props.searchText}
/>
<Button small transparent onPress={this.props.onClear}>
<Icon name="ios-close" />
</Button>
</Item>
<Button transparent onPress={this.props.onSearch}>
<Text>Search</Text>
</Button>
</Header>
);
}
}
class SearchWorld extends Component {
static navigationOptions = ({ navigation }) => ({
left: null,
header: () => {
const { state } = navigation;
return (
<SearchBar
onBackPress={() => navigation.goBack()}
onChangeText={text => {
state.params.onChangeText(text);
}}
onSearch={state.params.onSearch}
onClear={state.params.onClear}
searchText={state.params.searchText}
/>
);
}
});
onChangeText = text => {
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: text
});
this.props.onSearchTextChanged(text);
};
onSearch = () => {
Keyboard.dismiss();
const profile = { search: "test" };
const token = this.props.token;
this.props.searchForProfiles(token, profile);
};
onClear = () => {
this.props.onClear();
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: ""
});
};
componentDidMount() {
this.props.navigation.setParams({
onChangeText: this.onChangeText,
onSearch: this.onSearch,
onClear: this.onClear,
searchText: this.props.searchText
});
}
render() {
const { searchResults } = this.props;
let items = [];
if(searchResults && searchResults.data && searchResults.data.length > 0) {
items = [...searchResults.data];
}
return this.props.loading ? (
<Container style={{ alignItems: "center", justifyContent: "center" }}>
<Spinner color="#FE6320" />
</Container>
) : (
<Container>
<Content>
<List
style={{}}
dataArray={items}
renderRow={item => (
<ListItem style={{ marginLeft: 0}}>
<Text style={{paddingLeft: 10}}>{item.password}</Text>
</ListItem>
)}
/>
</Content>
</Container>
);
}
}
const mapStateToProps = state => {
const { token } = state.user;
const { searchText, searchResults, error, loading } = state.searchReaducer;
return {
token,
searchText,
searchResults,
error,
loading
};
};
export default connect(mapStateToProps, {
onClear,
onSearchTextChanged,
searchForProfiles
})(SearchWorld);
static navigationOptions = ({navigation}) => {
return {
headerTitle: () => <HeaderTitle />,
headerRight: () => (<Button iconLeft transparent small onPress = {navigation.getParam('onPressSyncButton')}>
<Icon style ={{color:'white', fontWeight:'bold'}} name='md-save' size = {32} />
<Text style ={{color:'white', fontWeight:'bold'}}>save</Text>
</Button>),
headerTintColor:'black',
headerStyle: {
backgroundColor: '#6200EE'
},
}
};
this.props.navigation.setParams({ onPressSyncButton: this.updateUserProfile });

react-native - Navigator and toolbarAndroid

Is there any way to set only one toolbarAndroid to be used on every screen of the application in conjunction with a navigator.
I set up a navigator in index.android.js :
import React, { Component } from 'react';
import {
AppRegistry,
Navigator,
} from 'react-native';
import ContactList from './src/containers/ContactList.js';
class MyIndex extends Component {
render() {
return (
<Navigator
initialRoute={{ name: 'index', component: ContactList }}
renderScene={(route, navigator) => {
if (route.component) {
return React.createElement(route.component, { navigator, ...route.props });
}
return undefined;
}}
/>
);
}
}
AppRegistry.registerComponent('reactest', () => MyIndex);
The first screen displays a contact list :
import React, { Component, PropTypes } from 'react';
import {
Text,
View,
TouchableOpacity,
TouchableHighlight,
ListView,
Image,
ActivityIndicator,
ToolbarAndroid,
} from 'react-native';
import styles from '../../styles';
import ContactDetails from './ContactDetails';
import logo from '../images/ic_launcher.png';
const url = 'http://api.randomuser.me/?results=15&seed=azer';
export default class ContactList extends Component {
static propTypes = {
navigator: PropTypes.object.isRequired,
}
constructor(props) {
super(props);
const datasource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
animating: false,
animatingSize: 0,
jsonData: datasource.cloneWithRows([]),
ds: datasource,
appTitle: 'Test',
appLogo: logo,
};
}
_handlePress() {
this.setState({
animating: true,
animatingSize: 80,
});
return fetch(url)
// convert to json
.then((response) => response.json())
// do some string manipulation on json
.then(({ results }) => {
const newResults = results.map((user) => {
const newUser = {
...user,
name: {
title: `${user.name.title.charAt(0).toUpperCase()}${user.name.title.slice(1)}`,
first: `${user.name.first.charAt(0).toUpperCase()}${user.name.first.slice(1)}`,
last: `${user.name.last.charAt(0).toUpperCase()}${user.name.last.slice(1)}`,
},
};
return newUser;
});
return newResults;
})
// set state
.then((results) => {
this.setState({
appSubTitle: 'Contacts list',
animating: false,
animatingSize: 0,
jsonData: this.state.ds.cloneWithRows(results),
});
});
}
renderRow(rowData: string) {
return (
<TouchableHighlight
onPress={() => {
this.props.navigator.push({
first: rowData.name.first,
component: ContactDetails,
props: {
title: rowData.name.title,
first: rowData.name.first,
last: rowData.name.last,
picture: rowData.picture.large,
thumbnail: rowData.picture.thumbnail,
},
});
}}
>
<View style={styles.listview_row}>
<Image
source={{ uri: rowData.picture.thumbnail }}
style={{ height: 48, width: 48 }}
/>
<Text>
{rowData.name.title} {rowData.name.first} {rowData.name.last}
</Text>
</View>
</TouchableHighlight>
);
}
render() {
const view = (
<View style={styles.container}>
<ToolbarAndroid
logo={this.state.appLogo}
title={this.state.appTitle}
subtitle={this.state.appSubTitle}
style={[{ backgroundColor: '#e9eaed', height: 56 }]}
/>
<ActivityIndicator
animating={this.state.animating}
style={[styles.centering, { height: this.state.animatingSize }]}
/>
<TouchableOpacity
onPress={() => this._handlePress()}
style={styles.button}
size="large"
>
<Text>Fetch results?</Text>
</TouchableOpacity>
<ListView
enableEmptySections
dataSource={this.state.jsonData}
renderRow={(rowData) => this.renderRow(rowData)}
onPress={() => this._handleRowClick()}
/>
</View>
);
return view;
}
}
and the second one displays a contact details :
import React, {
Component,
PropTypes,
} from 'react';
import {
Text,
View,
Image,
ToolbarAndroid,
} from 'react-native';
import styles from '../../styles';
export default class ContactDetails extends Component {
constructor(props) {
super(props);
this.state = {
animating: false,
animatingSize: 0,
appTitle: 'Test',
appLogo: { uri: this.props.thumbnail, height: 56 },
appSubTitle: `Contact Details - ${this.props.first} ${this.props.last}`,
};
}
render() {
return (
<View style={styles.container}>
<ToolbarAndroid
logo={this.state.appLogo}
title={this.state.appTitle}
subtitle={this.state.appSubTitle}
style={[{ backgroundColor: '#e9eaed', height: 56 }]}
/>
<Image
source={{ uri: this.props.picture }}
style={{ height: 128, width: 128 }}
/>
<Text>{this.props.title} {this.props.first} {this.props.last}</Text>
</View>
);
}
}
ContactDetails.propTypes = {
title: PropTypes.string.isRequired,
first: PropTypes.string.isRequired,
last: PropTypes.string.isRequired,
picture: PropTypes.string.isRequired,
thumbnail: PropTypes.string.isRequired,
};
I set up an toolbarAndroid in my first screen and another in my second screen, it's working well, but I have a feeling that it would be better to define only one toolbarAndroid and update it calling setState.
Is it possible, if so how ?
Wrap your Navigator class with your ToolbarAndroid. This way, everything that is rendered on the Navigator will be wrapped by the Toolbar. Actually, everything that is common between those scenes should be put on a single component wrapping the rest.
class MyIndex extends Component {
render() {
return (
<ToolbarAndroid>
<Navigator
initialRoute={{ name: 'index', component: ContactList }}
renderScene={(route, navigator) => {
if (route.component) {
return React.createElement(route.component, { navigator, ...route.props });
}
return undefined;
}}
/>
</ToolbarAndroid>
);
}
}
I managed to do that by wrapping the toolbarAndroid and the navigator in a view :
class MyIndex extends Component {
constructor(props) {
super(props);
this.state = {
appTitle: 'Test',
appLogo: logo,
};
}
render() {
return (
<View
style={styles.container}
>
<ToolbarAndroid
logo={this.state.appLogo}
title={this.state.appTitle}
subtitle={this.state.appSubTitle}
style={[{ backgroundColor: '#e9eaed', height: 56 }]}
/>
<Navigator
initialRoute={{ name: 'index', component: ContactList }}
renderScene={(route, navigator) => {
if (route.component) {
return React.createElement(route.component, { navigator, ...route.props });
}
return undefined;
}}
/>
</View>
);
}
}