'React Native Pass properties on navigator pop
I'm using NavigatorIOS on my react native app. I want to pass some properties when navigating back to previous route.
An example case: I'm in a form page. After submitting data, I want to go back to the previous route and do something based on the submitted data
How should I do that ?
Solution 1:[1]
Could you pass a callback func on the navigator props when you push the new route and call that with the form data before you pop to the previous route?
Solution 2:[2]
Code sample showing how to use a callback before pop. This is specifically for Navigator and not NavigatorIOS but similar code can be applied for that as well.
You have Page1 and Page2. You are pushing from Page1 to Page2 and then popping back to Page1. You need to pass a callback function from Page2 which triggers some code in Page1 and only after that you will pop back to Page1.
In Page1 -
_goToPage2: function() {
this.props.navigator.push({
component: Page2,
sceneConfig: Navigator.SceneConfigs.FloatFromBottom,
title: 'hey',
callback: this.callbackFunction,
})
},
callbackFunction: function(args) {
//do something
console.log(args)
},
In Page2 -
_backToPage1: function() {
this.props.route.callback(args);
this.props.navigator.pop();
},
The function "callbackFunction" will be called before "pop". For NavigatorIOS you should do the same callback in "passProps". You can also pass args to this callback. Hope it helps.
Solution 3:[3]
You can use AsyncStorage, save some value on child Component and then call navigator.pop():
AsyncStorage.setItem('postsReload','true');
this.props.navigator.pop();
In parent Component you can read it from AsyncStorage:
async componentWillReceiveProps(nextProps) {
const reload = await AsyncStorage.getItem('postsReload');
if (reload && reload=='true')
{
AsyncStorage.setItem('postsReload','false');
//do something
}
}
Solution 4:[4]
For NavigatorIOS you can also use replacePreviousAndPop().
Code:
'use strict';
var React = require('react-native');
var {
StyleSheet,
Text,
TouchableOpacity,
View,
AppRegistry,
NavigatorIOS
} = React;
var MainApp = React.createClass({
render: function() {
return (
<NavigatorIOS
style={styles.mainContainer}
initialRoute={{
component: FirstScreen,
title: 'First Screen',
passProps: { text: ' ...' },
}}
/>
);
},
});
var FirstScreen = React.createClass({
render: function() {
return (
<View style={styles.container}>
<Text style={styles.helloText}>
Hello {this.props.text}
</Text>
<TouchableOpacity
style={styles.changeButton} onPress={this.gotoSecondScreen}>
<Text>Click to change</Text>
</TouchableOpacity>
</View>
);
},
gotoSecondScreen: function() {
console.log("button pressed");
this.props.navigator.push({
title: "Second Screen",
component: SecondScreen
});
},
});
var SecondScreen = React.createClass({
render: function() {
return (
<View style={styles.container}>
<Text style={styles.helloText}>
Select a greeting
</Text>
<TouchableOpacity
style={styles.changeButton} onPress={() => this.sayHello("World!")}>
<Text>...World!</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.changeButton} onPress={() => this.sayHello("my Friend!")}>
<Text>...my Friend!</Text>
</TouchableOpacity>
</View>
);
},
sayHello: function(greeting) {
console.log("world button pressed");
this.props.navigator.replacePreviousAndPop({
title: "First Screen",
component: FirstScreen,
passProps: {text: greeting}
});
}
});
var styles = StyleSheet.create({
mainContainer: {
flex: 1,
backgroundColor: "#eee"
},
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
marginTop: 50,
},
helloText: {
fontSize: 16,
},
changeButton: {
padding: 5,
borderWidth: 1,
borderColor: "blue",
borderRadius: 4,
marginTop: 20
}
});
AppRegistry.registerComponent("TestApp", () => MainApp);
You can find the working example here: https://rnplay.org/apps/JPWaPQ
I hope that helps!
Solution 5:[5]
I had the same issue with React Native's navigator which I managed to solve using EventEmitters and Subscribables. This example here was really helpful: https://colinramsay.co.uk/2015/07/04/react-native-eventemitters.html
All I needed to do was update for ES6 and the latest version of React Native.
Top level of the app:
import React, { Component } from 'react';
import {AppRegistry} from 'react-native';
import {MyNavigator} from './components/MyNavigator';
import EventEmitter from 'EventEmitter';
import Subscribable from 'Subscribable';
class MyApp extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.eventEmitter = new EventEmitter();
}
render() {
return (<MyNavigator events={this.eventEmitter}/>);
}
}
AppRegistry.registerComponent('MyApp', () => MyApp);
In the _renderScene function of your navigator, make sure you include the "events" prop:
_renderScene(route, navigator) {
var Component = route.component;
return (
<Component {...route.props} navigator={navigator} route={route} events={this.props.events} />
);
}
And here is the code for the FooScreen Component which renders a listview.
(Note that react-mixin was used here in order to subscribe to the event. In most cases mixins should be eschewed in favor of higher order components but I couldn't find a way around it in this case):
import React, { Component } from 'react';
import {
StyleSheet,
View,
ListView,
Text
} from 'react-native';
import {ListItemForFoo} from './ListItemForFoo';
import reactMixin from 'react-mixin'
import Subscribable from 'Subscribable';
export class FooScreen extends Component {
constructor(props) {
super(props);
this._refreshData = this._refreshData.bind(this);
this._renderRow = this._renderRow.bind(this);
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([])
}
}
componentDidMount(){
//This is the code that listens for a "FooSaved" event.
this.addListenerOn(this.props.events, 'FooSaved', this._refreshData);
this._refreshData();
}
_refreshData(){
this.setState({
dataSource: this.state.dataSource.cloneWithRows(//YOUR DATASOURCE GOES HERE)
})
}
_renderRow(rowData){
return <ListItemForFoo
foo={rowData}
navigator={this.props.navigator} />;
}
render(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
)
}
}
reactMixin(FooScreen.prototype, Subscribable.Mixin);
Finally. We need to actually emit that event after saving a Foo:
In your NewFooForm.js Component you should have a method like this:
_onPressButton(){
//Some code that saves your Foo
this.props.events.emit('FooSaved'); //emit the event
this.props.navigator.pop(); //Pop back to your ListView component
}
Solution 6:[6]
This is an old question, but currently React Navigation's documentation for Passing params to a previous screen suggests that we use navigation.navigate() and pass whatever parameters we want the previous screen to have.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Tarrence |
Solution 2 | mutp |
Solution 3 | Vladislav |
Solution 4 | |
Solution 5 | Kenji Crosland |
Solution 6 | harrolee |