I am having a problem when I installState in multiple components based on the same key in AsyncStorage. Since the state is set in the DidMount component, and these components are not necessarily unmounted and mounted in the navigation, the state value and the AsyncStorage value can go out of sync.
Here is the simplest example I could do.
Component A
A just installs the navigation and application.
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 to mount, and then sets the 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. The change 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 switch to C and then return to B, the state in B and the value in AsyncStorage are now out of sync. As far as I can tell, navigator.pop () does not start the component life cycle functions that I can use to tell B to update the value.
One solution that I know about but is not ideal is to get B to point to C and give C the inverse function to switch. This will work well if B and C will always be directly parents and children, but in a real application the navigation hierarchy can be much deeper.
Is there a way to call a function on a component after a navigation event or something else that I am missing?