I am having an issue where I setState in multiple components based on the same key in AsyncStorage. Since the state is set in componentDidMount, and these components don't necessarily unmount and mount on navigation, the state value and the AsyncStorage value can get out of sync.
Here is the simplest example I could make.
Component A
A just sets up the navigation and app.
var React = require('react-native');
var B = require('./B');
var {
AppRegistry,
Navigator
} = React;
var A = React.createClass({
render() {
return (
<Navigator
initialRoute={{
component: B
}}
renderScene={(route, navigator) => {
return <route.component navigator={navigator} />;
}} />
);
}
});
AppRegistry.registerComponent('A', () => A);
Component B
B reads from AsyncStorage on mount, and then sets to state.
var React = require('react-native');
var C = require('./C');
var {
AsyncStorage,
View,
Text,
TouchableHighlight
} = React;
var B = React.createClass({
componentDidMount() {
AsyncStorage.getItem('some-identifier').then(value => {
this.setState({
isPresent: value !== null
});
});
},
getInitialState() {
return {
isPresent: false
};
},
goToC() {
this.props.navigator.push({
component: C
});
},
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>
{this.state.isPresent
? 'Value is present'
: 'Value is not present'}
</Text>
<TouchableHighlight onPress={this.goToC}>
<Text>Click to go to C</Text>
</TouchableHighlight>
</View>
);
}
});
module.exports = B;
Component C
C reads the same value from AsyncStorage as B, but allows you to change the value. Changing toggles both the value in state and in AsyncStorage.
var React = require('react-native');
var {
AsyncStorage,
View,
Text,
TouchableHighlight
} = React;
var C = React.createClass({
componentDidMount() {
AsyncStorage.getItem('some-identifier').then(value => {
this.setState({
isPresent: value !== null
});
});
},
getInitialState() {
return {
isPresent: false
};
},
toggle() {
if (this.state.isPresent) {
AsyncStorage.removeItem('some-identifier').then(() => {
this.setState({
isPresent: false
});
})
} else {
AsyncStorage.setItem('some-identifier', 'some-value').then(() => {
this.setState({
isPresent: true
});
});
}
},
goToB() {
this.props.navigator.pop();
},
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>
{this.state.isPresent
? 'Value is present'
: 'Value is not present'}
</Text>
<TouchableHighlight onPress={this.toggle}>
<Text>Click to toggle</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.goToB}>
<Text>Click to go back</Text>
</TouchableHighlight>
</View>
);
}
});
module.exports = C;
If you toggle in C and then return to B, the state in B and value in AsyncStorage are now out of sync. As far as I can tell, navigator.pop() does not trigger any component lifecycle functions I can use to tell B to refresh the value.
One solution I am aware of, but isn't ideal, is to make B's state a prop to C, and give C a callback prop to toggle it. That would work well if B and C would always be directly parent and child, but in a real app, the navigation hierarchy could be much deeper.
Is there anyway to trigger a function on a component after a navigation event, or something else that I'm missing?