Passing props between views in React Native - react-native

I am trying to simply pass props between views in my React-Native app. Namely, I am trying to collect data from a text input, and pass it to the next view in a string. This is what I have:
first view:
constructor(props) {
super(props);
this.state = {name: "", email: "" };
}
_pressRow(){
let name=this.state.name;
let email=this.state.email;
this.props.navigator.push({
ident: "ImportContactScreen",
sceneConfig: Navigator.SceneConfigs.FloatFromRight,
passProps: { name: name}
});
}
<TextInput
style={styles.input}
ref= "name"
onChangeText={(name) => this.setState({name})}
value={this.state.name}
/>
<F8Button
style={styles.button}
onPress={() => this._pressRow()}
caption="Continue"
/>
second view:
props = {name}
<Text style={styles.h1}>Hi, {this.props.name}. Let`'`s import your contacts:</Text>
The navigator I'm using looks like:
class MyAppNavigator extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<Navigator
ref="appNavigator"
initialRoute={this.props.initialRoute}
renderScene={this._renderScene}
configureScene={(route) => ({
...route.sceneConfig || Navigator.SceneConfigs.FloatFromRight
})}
/>
);
}
_renderScene(route, navigator) {
var globalNavigatorProps = { navigator }
switch (route.ident) {
case "LoginScreen":
return <LoginScreen {...globalNavigatorProps} />
case "UserFlowScreen":
return <UserFlowScreen {...globalNavigatorProps} />
case "ImportContactScreen":
return <ImportContactScreen {...globalNavigatorProps} />
default:
return <LoginScreen {...globalNavigatorProps} />
}
}
};
module.exports = MyAppNavigator;
When I run this, this.props.name comes up blank

passProps is a property of route. Try adding {...route.passProps} to each return in your switch statement:
return <LoginScreen {...globalNavigatorProps} {...route.passProps} />

Related

How do you use checkbox in map function in React Native?

My current code is like this.
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
checkedItems: new Map(),
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id) {
this.setState((prevState) => ({ checkedItems: prevState.checkedItems.set(id, true) }));
console.log(this.state.checkedItems);
}
async componentDidMount() {
const items = ...;
this.setState({ items });
}
render() {
const { items } = this.state;
return (
<Container>
<Content>
<View>
{items.map((item) => (
<View key={item.id}>
<Text>{item.name}</Text>
<View>
<CheckBox
checked={this.state.checkedItems.get(item.id)}
onPress={() => this.handleChange(item.id)}
/>
</View>
</View>
))}
</View>
</Content>
</Container>
);
}
}
I would like to know how to uncheck.
Also, when I firstly check, console.log() in handleChange() outputs Map {}.
Is it correct?
Or if you know better way bay to use checkbox in map function, plz tell me.
I would appreciate it if you could give me advices.

Passing data back to component by react-native navigation

I have main App.js where i implemented StackNavigator:
export default class App extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="MainPage" component={MainPage} />
<Stack.Screen name="Submit" component={Submit} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
What i want to do is Pass data from Submit component into my MainPage and display it in ScrollView from array: arr in state of MainPage.
Mainpage looks like this. I want to pass function addNode to adding node into state of my MainPage:
class MainPage extends Component {
constructor(props) {
super(props);
this.state = {
arr: [],
liters: '',
time: '',
};
}
addNode = (time, liters) => {
if (this.state.time) {
this.state.arr.push({calc: time + liters});
this.setState({time: ''});
this.setState({liters: ''});
}
};
onPress = () => {
this.props.navigation.navigate('Submit', {addNode: this.addNode});
};
render() {
return (
<View>
<ScrollView>{this.state.arr}</ScrollView>
<TouchableOpacity onPress={this.onPress}>
<Text>+</Text>
</TouchableOpacity>
</View>
);
}
}
My Submit component looks like this. on button Submit i want to pass data from inputs to MainPage, add it to array and then display it in scroll view:
class Submit extends Component {
constructor(props) {
super(props);
this.state = {
liters: '',
time: '',
};
}
goBack() {
this.props.navigation.goBack();
this.props.navigation.state.params.addNode({
time: this.state.time,
liters: this.state.liters,
});
}
render() {
return (
<View>
<Text>Form</Text>
<TextInput
onChangeText={time => this.setState({time})}
value={this.state.time}
/>
<TextInput
onChangeText={liters => this.setState({liters})}
value={this.state.liters}
/>
<Button title="Submit" onPress={this.goBack} />
</View>
);
}
}
When i click Submit from my Submit component i get error presented in this screen:
My question is, how to pass data back to parent component from children component using StackNavigator? What im doing wrong here?

React Native reusable edit component

I'm trying to create a reusable component in react native. The idea is to have only one component responsible to edit all the fields that I have.
Main Component
...
constructor(props) {
super(props);
this.state.FirstName = 'Joe'
}
...
const { FirstName } = this.state.FirstName;
<TouchableOpacity
onPress={() =>
NavigationService.navigate('EditData', {
label: 'First Name',
initialValue: FirstName,
onSubmit: (FirstName) => this.setState({ FirstName })
})
}
>
<CardItem>
<Left>
<FontAwesome5 name="user-edit" />
<Text>First Name</Text>
</Left>
<Right>
<Row>
<Text style={styles.valueText}>{FirstName} </Text>
<Icon name="arrow-forward" />
</Row>
</Right>
</CardItem>
</TouchableOpacity>
// Keep doing the same for other fields
Then, the edit component should be reusable.
constructor(props) {
super(props);
// callback function
this.onSubmit = props.navigation.getParam('onSubmit');
// label/value
this.state = {
label: props.navigation.getParam('label'),
value: props.navigation.getParam('initialValue')
};
}
render() {
const { onSubmit } = this;
const { label, value } = this.state;
return (
<Container>
<Header />
<Content>
<Item floatingLabel style={{ marginTop: 10 }}>
<Label>{label}</Label>
<Input
value={value}
onChangeText={val => this.setState({ value: val })}
/>
</Item>
<Button
onPress={() => {
onSubmit(value);
NavigationService.navigate('TenantDetails');
}
}
>
<Text>OK</Text>
</Button>
</Content>
</Container>
);
}
When back to the main component, the first name value was not changed.
My NavigationService in case it might be the problem:
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
};
Thanks
You could pass a callback to your new component which handles this. The new component would start with a state with the initialValue set. It looks like you might be using react-navigation so I would recommend that if you want this component on its own screen you could do
this.navigation.navigate('SetValueScreen', {
initialValue: this.state.email,
onSubmit: (email) => this.setState({ email })
})
and on the SetValueScreen get the initialValue in the constructor and in the render use the callback
class SetValueScreen extends React.PureComponent{
constructor(props){
super(props)
this.onSubmit = props.navigation.getParam('onSubmit');
this.state = {
value: props.navigation.getParam('initialValue')
}
}
render(){
const { onSubmit } = this
const { value } = this.state
return (
...
<Right>
<TextInput value={value} onChangeText={(value) => setState({ value })} />
</Right>
<Button onPress={() => {
onSubmit(value)
navigation.goBack()
}} >
OK
</Button>
...
)
}
}
I hope this helps.

Cant navigate to next screen

I am trying to navigates screens with a stack navigator. The Idea is, my app will navigate from my list of chats, too the chat screen. However when I try to navigate to the next screen, I receive an error saying "undefined is not an object" on this.props.navigation. Here is what my code looks like:
MainTabNavigator (Contains my stack navigator)
const ChatListStack = createStackNavigator({
ChatList:ChatListScreen,
ChatView:ChatScreen,
});
ChatListStack.navigationOptions = {
tabBarLabel: 'ChatList',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-options${focused ? '' : '-outline'}` :
'md-options'}
/>
),
};
ChatListScreen (Where the navigation starts from)
export default class ChatListScreen extends Component {
constructor(props) {
super(props);
}
static navigationOptions = {
title: "Chats"
};
renderRow({ item }) {
return (
<TouchableHighlight
onPress={() => this.props.navigation.navigate("ChatView")}
>
<ListItem
roundAvatar
title={item.name}
subtitle={item.subtitle}
avatar={{ uri: item.avatar_url }}
/>
</TouchableHighlight>
);
}
goToChat() {}
render() {
return (
<View style={styles.mainContainer}>
<SearchBar
lightTheme
icon={{ type: "font-awesome", name: "search" }}
placeholder="Type Here..."
/>
<List style={styles.listContainerStyle}>
<FlatList
data={users}
renderItem={this.renderRow}
keyExtractor={item => item.name}
/>
</List>
</View>
);
}
}
Chat(This is the target Chat screen)
export default class ChatScreen extends Component {
constructor() {
super();
this.state = {
messages: []
};
}
componentWillMount() {
this.setState({
messages: [
{
_id: 1,
text: "Hello test",
createdAt: new Date(),
user: {
_id: 2,
name: "dude"
}
}
]
});
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={message => this.onSend(message)}
user={{
_id: 1
}}
/>
);
}
}
to solve this problem you must make a function to handle navigation then bind it in the contractor then send it in your Touchableopacity

Pass value between component in React Native Navigator

How can I pass data from sceneA to sceneB with Navigator in React Native?
I'm using this to go to sceneB:
this.props.navigator.push({
id: "MainPage",
name: 'mainPage'
});
You need to set up the passProps property on the navigator. There are a few recent examples on stack overflow, specifically here and here.
<Navigator
initialRoute={{name: 'Main', component: Main, index: 0}}
renderScene={(route, navigator) => {
return React.createElement(<YourComponent />, { ...this.props, ...route.passProps, navigator, route } );
}} />
or
<Navigator
initialRoute={{name: 'Main', component: Main, index: 0}}
renderScene={(route, navigator) => {
<route.component {...route.passProps} navigator={navigator} route={route} />
}
}
/>
If you are looking for the most basic of setups just to understand the functionality, I have set up a project here that you can reference, and pasted the code below.
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
Image,
TouchableHighlight, TouchableOpacity
} = React;
class Two extends React.Component {
render(){
return(
<View style={{marginTop:100}}>
<Text style={{fontSize:20}}>Hello From second component</Text>
<Text>id: {this.props.id}</Text>
<Text>name: {this.props.name}</Text>
<Text>name: {this.props.myVar}</Text>
</View>
)
}
}
class Main extends React.Component {
gotoNext(myVar) {
this.props.navigator.push({
component: Two,
passProps: {
id: 'page_user_infos',
name: 'page_user_infos',
myVar: myVar,
}
})
}
render() {
return(
<View style={{flex: 4, flexDirection: 'column', marginTop:100}}>
<TouchableHighlight style={{ height:40, borderWidth:1, marginBottom:10, backgroundColor: '#ddd'}} name='Pole' onPress={ () => this.gotoNext('This is a property that is being passed') }>
<Text style={{textAlign:'center'}}>Go to next page</Text>
</TouchableHighlight>
</View>
)
}
}
class App extends React.Component {
render() {
return (
<Navigator
style={{flex:1}}
initialRoute={{name: 'Main', component: Main, index: 0}}
renderScene={(route, navigator) => {
if (route.component) {
return React.createElement(route.component, { ...this.props, ...route.passProps, navigator, route } );
}
}}
navigationBar={
<Navigator.NavigationBar routeMapper={NavigationBarRouteMapper} />
} />
);
}
}
var NavigationBarRouteMapper = {
LeftButton(route, navigator, index, navState) {
if(index > 0) {
return (
<TouchableHighlight style={{marginTop: 10}} onPress={() => {
if (index > 0) {
navigator.pop();
}
}}>
<Text>Back</Text>
</TouchableHighlight>
)} else {
return null}
},
RightButton(route, navigator, index, navState) {
return null;
},
Title(route, navigator, index, navState) {
return null
}
};
var styles = StyleSheet.create({
});
AppRegistry.registerComponent('App', () => App);
I would set up your navigator as this example. Essentially, put the navigator in your index scene and have all the necessary components imported there. Then, define a renderScene() function to handle all the routes based on a name (or id, like you did). I would not use the component object as the thing itself that is passed from the call to the navigator push method because if you do that you will have to import the component in that specific view. I used to do that and ran into many problems, so I am just warning you if you run into any issues, consider this approach.
renderScene(route, navigator) {
if(route.name == 'root') {
return <Root navigator={navigator} />
}
if(route.name == 'register') {
return <Register navigator={navigator} />
}
if(route.name == 'login') {
return <Login navigator={navigator} />
}
if(route.name == 'home') {
return <Home navigator={navigator} {...route.passProps} />
}
if(route.name == 'update') {
return <Update navigator={navigator} {...route.passProps} />
}
}
Sometimes the pass prop doesn't work (at least for me) So what I do is I pass it in the route object itself
nav.push({
id: 'MainPage',
name: 'LALA',
token: tok
});
so to access it in the next scene I use
var token = this.props.navigator.navigationContext.currentRoute.token;
although kind of a complicated "access" but it is fool proof pass props might work or might not also in this way you can pass the properties in the route object itself thus saving you from the hassle of sending it in a different way.
This might not be the right way but I find it a tad bit more convenient.
If you are using react-native-navigation, simply add passProps to your params object, according to documentation.
eg.:
this.props.navigator.push({
screen: 'example.Screen',
title: 'Screen Title',
passProps: {
id: 'MainPage',
name: 'mainPage'
}
})
You can also pass parameters to sceneA to sceneB using following way. Assume that _handlePressOnClick() is written on some button click in SceneA screen.
_handlePressOnClick(){ //this can be anything
this.props.navigator.push({
name: "sceneB", //this is the name which you have defined in _renderScene
otherData: {
dataA: "data1"
}
})
}
Then defined data in your _renderScene where you have implemented your navigator like this way
_renderScene(route, navigator) {
switch(route.name) {
case "sceneA":
return (
<sceneA navigator={navigator} />
);
break;
case "sceneB":
return (
<sceneB navigator={navigator} otherData={route.otherData}/>
);
break;
}
Don't forget to import your views files like this
import sceneA from './sceneA';
import sceneB from './sceneB';
Now in sceneB file you can access your otherData following way
var {dataA} = this.props.otherData;
or
constructor(props){
super(props);
this.state={
otherData: this.props.otherData
}
}
then in your render() method you can access otherData using state.
<Text>{this.state.otherData.dataA}</Text>
You can also use to maintain global level state using redux which is accessible through action and props automatically.
1.While passing the data from scene A to scene B, you must specify in the navigator tag that you are also passing the data with route.
ex.
if(route.name == 'home') {
return <Home navigator={navigator} data={route.data} />
}
send data from scene A
ex.this.props.navigator.push({name:'sceneB',data:{name:this.state.name},});
in scene B access data like this
ex. this.props.data.name
and you get the data from scene A to scene B