I've edited a little bit the example from the react navigation official guide , in order to simulate a state change after 1 second in the HomeScreen.
I can't understand why the DetailScreen is not rerenderer when the parent screen state changes. Any way to obtain such behaviour?
import React from 'react';
import { Button, View, Text } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
class HomeScreen extends React.Component {
state = { value: 10 }
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Click"
onPress={() => {
const { value } = this.state;
this.props.navigation.navigate('Detail', { value });
setTimeout(() => {
this.setState({ value: value + 1 })
}, 1000);
}}
/>
</View>
);
}
}
class DetailScreen extends React.Component {
render() {
const { navigation } = this.props;
const value = navigation.getParam('value');
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Text>{value}</Text>
</View>
);
}
}
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Detail: DetailScreen,
},
{
initialRouteName: 'Home',
}
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
Snack here
Why your code isn't working:
You're passing value
as a navigation prop from Home
to Details
. Unlike regular props, changing the value of a navigation prop in Home
doesn't cause the value of the navigation prop itself to change. So if Home
were a parent component that had Details
as a child component, like this:
class HomeScreen extends React.Component {
...
<DetailsScreen
value: this.state.value
/>
...
}
then when this.state.value
changes in Home
, this.props.value
automatically changes in Details
. However, since Home
and Details
have a sibling relationship in the stack navigator, you aren't able to pass value
as a regular prop; the only way to pass a prop from Home
to Details
is, as you've done, as a navigation param. The problem is that when you pass value
as you've done:
const { value } = this.state;
this.props.navigation.navigate('Detail', { value });
updating this.state.value
in Home
does not cause this.props.navigation.getParam('value')
to automatically update. So in your code:
const value = navigation.getParam('value');
<Text>{value}</Text>
value
remains what it was when it was initially passed.
SOLUTION
There are several possible workarounds, like manually forcing a re-render after the setTimeout
or re-structuring your component hierarchy to make Home
the parent of Details
. However, I think the best way to solve the problem while preserving the structure of your app is the following:
Instead of holding this.state.value
in Home
, hold it in App
. This follows the general principle that it's easier for a child to update a parent's state variables (or vice versa) than it is for a component to update its sibling's state variables.
Update to App
component
Since App
is an AppContainer
, you'll need to pass this.state.value to Details
via screenprops. When you create any kind of navigator, screenprops are the way to pass variables to all the components in the navigator. So your App
component will now look like this:
export default class App extends React.Component {
state = {value: 10} // state in App now instead of Home
updateValue = (value) => { // method to update App's state, passed to children
this.setState({value})
}
render() {
return <AppContainer screenProps={{
parentState: this.state,
updateValue: this.updateValue
}}/>;
}
}
Update to Home
component
The only thing you'll change in your Home
component will be the onPress
function. First, you won't pass value
as a navigation prop anymore since you'll be accessing the value as a screenProps passed from App
to all screens, rather than as a navigation prop passed from Home
to Details
. Second, instead of updating this.state
of Home
you'll be calling this.props.screenProps.updateValue()
to update the state in App
. So the onPress
in your Home
component will now look like this:
onPress={() => {
this.props.navigation.navigate('Detail'); // no navigation prop
setTimeout(() => {
screenProps.updateValue(screenProps.appState.value + 1) // updating state of App rather than state of Home
}, 1000);
}}
Update to Details
component
The only change to Details
is that rather than displaying this.props.navigation.getParam('value')
, you'll be displaying this.props.screenProps.appState.value
since we're now getting value
from the screenProps from App
instead of as a navigation prop from Home
. So your Details
component will now look like this:
class DetailScreen extends React.Component {
render() {
const { screenProps } = this.props;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Text>{screenProps.appState.value}</Text>
</View>
);
}
}
WHOLE NEW CODE
import React from 'react';
import { Button, View, Text } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
class HomeScreen extends React.Component {
state = { value: 10 }
render() {
const { screenProps } = this.props;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Click"
onPress={() => {
this.props.navigation.navigate('Detail'); // no navigation prop
setTimeout(() => {
screenProps.updateValue(screenProps.appState.value + 1) // updating state of App rather than state of Home
}, 1000);
}}
/>
</View>
);
}
}
class DetailScreen extends React.Component {
render() {
const { screenProps } = this.props;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Text>{screenProps.appState.value}</Text>
</View>
);
}
}
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Detail: DetailScreen,
},
{
initialRouteName: 'Home',
}
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
state = {value: 10} // state in App now instead of Home
updateValue = (value) => { // method to update App's state, passed to children
this.setState({value})
}
render() {
return <AppContainer screenProps={{
appState: this.state,
updateValue: this.updateValue
}}/>;
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.