How to jump another page after login/sign up - react-native

I build navigation with react-navigation v3 and auth with firebase. No problem with that. Navigation flow is works and I can sign up. The problem I am facing is, when I push the Sign up button it doesn't jump to Signup Screen.
So the structure that Build: in App.js I am doing navigation part. First sending Welcome Screen which include Login.
This is Welcome Screen:
import React, { Component } from 'react'
import {StyleSheet, View } from "react-native";
import {Container, Text, Form, Content, Header, Button, Input, Label, Item} from 'native-base';
import SignUp from '../screens/register/SignUp'
import * as firebase from 'firebase';
const firebaseConfig = {
apiKey: "example example",
authDomain: "example example",
databaseURL: "example example",
projectId: "example example",
storageBucket: "example example",
};
firebase.initializeApp(firebaseConfig);
export default class WelcomeScreen extends Component {
constructor(props){
super(props)
this.state = ({
email: '',
password: ''
})
}
loginUser = (email, password, navigate) => {
try {
firebase.auth().signInWithEmailAndPassword(email,password).then(function(user){
console.log(user);
navigate('Learn')
})
}
catch (error) {
console.log(error.toString())
}
};
render() {
return (
<Container style={styles.container}>
<Form>
<Item floatingLabel>
<Label>E-mail</Label>
<Input
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(email) => this.setState({email})}
/>
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry={true}
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(password)=>this.setState({password})}
/>
</Item>
</Form>
<Button style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.loginUser(this.state.email,this.state.password)}
rounded
success
>
<Text>Kelimeda'ya Uç</Text>
</Button>
<Button style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.props.navigation.navigate('SignUp')}
rounded
primary
>
<Text>Beni Kaydet!</Text>
</Button>
</Container>
);
}
}
Sign Up Screen:
import React, { Component } from 'react'
import {StyleSheet, View } from "react-native";
import {Container, Text, Form, Content, Header, Button, Input, Label, Item} from 'native-base';
import * as firebase from 'firebase';
export default class WelcomeScreen extends Component {
constructor(props){
super(props)
this.state = ({
email: '',
password: ''
})
}
signUpUser = (email, password) => {
try {
if(this.state.password.length < 6){
alert('Lutfen 6 dan daha uzun bir karakter giriniz.')
return
}
firebase.auth().createUserWithEmailAndPassword(email,password)
}
catch (error) {
console.log(error.toString())
}
};
render() {
return (
<Container style={styles.container}>
<Form>
<Item floatingLabel>
<Label>E-mail</Label>
<Input
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(email) => this.setState({email})}
/>
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry={true}
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(password)=>this.setState({password})}
/>
</Item>
</Form>
<Button style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.signUpUser(this.state.email,this.state.password)}
rounded
primary
>
<Text>Beni Kaydet!</Text>
</Button>
</Container>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 5,
justifyContent: 'center',
backgroundColor: '#fff',
},
});
and this is the App Screen. Do I need to check here user Logged in or Not? or Welcome Screen?
//imports...
import React, { Component } from 'react';
import {View, StatusBar} from 'react-native';
import {
createSwitchNavigator,
createAppContainer,
createDrawerNavigator,
createBottomTabNavigator,
createStackNavigator,} from 'react-navigation';
import Icon from 'react-native-vector-icons/Ionicons';
import WelcomeScreen from './src/screens/Welcome';
import Learn from './src/screens/Tab/Learn';
import Settings from './src/screens/Tab/Settings';
import Play from './src/screens/Tab/Play';
//content: const & functions and styles...
const DashboardTabNavigator = createBottomTabNavigator({
Play,
Learn,
Settings
},
{
navigationOptions: ({navigation}) => {
const {routeName} = navigation.state.routes
[navigation.state.index];
return {
headerTitle: routeName,
headerTintColor:'#fff',
headerStyle:{
backgroundColor: '#2c3e50',
}
};
}
});
const DashStack = createStackNavigator({
DashboardTabNavigator: DashboardTabNavigator
}, {
defaultNavigationOptions:({navigation}) =>{
return {
headerLeft: <Icon
style={{paddingLeft: 15, color:'#fff'}}
onPress={()=>navigation.openDrawer()}
name={'md-menu'}
size={30}
/>
}
},
});
const appDrawNavigator = createDrawerNavigator({
Dashboard:{ screen: DashStack }
});
const appSwitchNavigation = createSwitchNavigator({
Welcome:{ screen: WelcomeScreen },
Dashboard:{ screen: appDrawNavigator }
});
const AppContainer = createAppContainer(appSwitchNavigation);
class App extends Component {
render() {
return(
<View style={{flex: 1}}>
<StatusBar
backgroundColor="blue"
barStyle="light-content"
/>
<AppContainer/>
</View>
) ;
}
}
export default App;

Your login function looks like this:
loginUser = (email, password, navigate) => {
try {
firebase.auth().signInWithEmailAndPassword(email,password).then(function(user){
console.log(user);
navigate('Learn')
})
}
catch (error) {
console.log(error.toString())
}
};
This function expecting three parameters. You should pass this.props.navigation.navigate to the login function to use navigate('Learn')
<Button
style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.loginUser(this.state.email,this.state.password,this.props.navigation.navigate)}
rounded
success
>

Solution for problem 1: You have not passed navigate to your loginUser function, so it is not working. Please send the navigate param to the loginUser like this.
<Button
style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.loginUser(this.state.email,this.state.password, this.props.navigation.navigate)}
rounded
success>
Solution for problem 2: For the firebase duplication issue, its because you are initialising the firebase instance twice in the application. What you should do instead is, just initialize the firebase at the application root component like App.js or Splash screen, so that it can be available throughout the app lifecycle and whenever you need to use it just import it and use.
Solution for problem 3: Its a common usecase as to know the logged in status of the user upfront to navigate the user appropriately into the application.
For this, what you can do is, just save a flag in AsyncStorage for eg. isLoggedIn as YES upon successful login, and post this, whenever the app is opened just analyse with the flag's presence/value wether the user is logged in or not. A good place to do this is either your app's Splashscreen component or the root component of the application.
Edited Answer (additional): (for problem 1)
You have your navigation routes nested wrongly to directly jump to Learn from
welcome screen, to navigate from one screen to another, the two screen should be in same navigation scope (if navigator is given as a route in another navigator then the route navigator is considered as a screen and the user will be navigated to its initial route when navigated to it)
Your code should target navigating to Dashboard route, which will internally render the nested navigators i.e. the first/initial route but with current nesting this will land you to Play so what can be done is, make Learn as first/initial route of your tab navigator.
The code in the loginUser should be navigate('Dashboard') and your tab navigator should have Learn as its initial route.

Related

React native navigation: '_this2.props.navigation.navigate'

my App.js code
import React from 'react'
import { Icon } from 'react-native-elements'
import { StyleSheet, View, StatusBar } from 'react-native'
import { createAppContainer } from 'react-navigation'
import { createStackNavigator } from 'react-navigation-stack'
import HomeScreen from './screens/homeScreen'
import LoginScreen from './screens/loginScreen'
import SignupScreen from './screens/signup'
import Screen2 from './screens/screen2'
import Screen1 from './screens/screen1'
export default class App extends React.Component {
setLogged(){
this.setState({logged:true})
this.forceUpdate()
}
//change to false when want to enable login feature else true
state = {
logged: false,
}
render() {
if(this.state.logged){
return(
<View style={styles.container} >
<StatusBar hidden = {false}/>
<AppContainer />
</View>
)
}else{
return(
<View style={styles.container} >
<StatusBar hidden = {false}/>
<LoginScreen signup={()=>this.props.navigation.navigate('Signup')} success={()=>this.setLogged()} />
</View>
)
}
}
}
const styles = StyleSheet.create({
container: {
position: 'relative',
width: '100%',
height: '100%',
backgroundColor: '#000000',
},
})
const HomeAppContainer = createStackNavigator(
{
Home: {screen: HomeScreen},
Signup: { screen: SignupScreen },
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2 },
},
{initialRouteName: "Home"},
);
const AppContainer = createAppContainer(HomeAppContainer)
and the login screen contains
import React, { Component } from 'react';
import { StyleSheet, View, Text, TouchableOpacity, TextInput, ToastAndroid } from 'react-native';
import Colors from '../constants/colors'
import GLOBAL from '../constants/global'
export default class LoginScreen extends Component {
state = {
email: '',
password: '',
}
login() {
if (userid == 'admin') {
ToastAndroid.show('Invalid email/password', ToastAndroid.SHORT);
} else {
GLOBAL.userid = userid;
this.props.success()
}
})
}
render() {
return (
<View style={styles.MainContainer}>
<Text style={styles.text}>Email:</Text>
<View style={styles.inputView}>
<TextInput
style={styles.inputs}
autoFocus
returnKeyType="next"
keyboardType="email-address"
onChangeText={(email) => { this.setState({ email: email }) }}
/>
</View>
<Text style={styles.text}>Password:</Text>
<View style={styles.inputView}>
<TextInput
style={styles.inputs}
secureTextEntry
onChangeText={(password) => { this.setState({password:password}) }}
/>
</View>
<View style={styles.buttonGroup}>
<TouchableOpacity
style={styles.button}
onPress={() => { this.login() }} >
<Text style={{ fontSize: 24 }}>Sign in</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => { this.props.signup() }}>
<Text style={{ fontSize:24 }}>Sign up</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
//my styles here
});
the error is as follows
TypeError: undefined is not an object(evaluating '_this2.props.navigation.navigate')
i am learning react-native and making this login screen.
The app will check if the user is already logged in. if not then he will be prompted login screen which will have sign up option.
If the user is already logged in the app will directly go to home screen where screen1 and screen2 is used in stack
Well, the navigation prop is only passed down the navigation tree. Your App component is not a navigation screen like Home/Signup... (it doesn't have a route)... So basically App is not part of a navigator, it's not created using createStackNavigator or the others.
So, in conclusion the navigation prop is not defined.
Basically, how to fix this: LoginScreen should also be a route inside a navigator, therefore the navigation prop will be defined. Your App component should not render anything besides the <AppContainer /> and further logic should be handled in the first screen defined in the routes, which in your case might be the LoginScreen. In there you'll check if the user is logged in or not and navigate accordingly.
There's a great guide on their website on how to accomplish authentication flow: https://reactnavigation.org/docs/en/4.x/auth-flow.html

Passing of variables assigned from one screen to another

basically I need to pass over a parameter called tempRole over from Login to MainTabNavigator and create tabs accordingly to user role. For example, vendor have 4 tabs and others just 3 tabs. However, I can't seem to get the role passed over though.
From Login
import React from 'react';
import { Text, View, StyleSheet, Platform, TextInput, TouchableOpacity } from 'react-native';
import firebase from 'firebase';
import { Container, Form, Item, Label, Input, Button } from "native-base";
import * as FirebaseAPI from '../modules/firebaseAPI';
import MainTabNavigator from '../navigation/MainTabNavigator';
import bottomTabNavigator from '../navigation/MainTabNavigator';
export default class LoginScreen extends React.Component {
constructor(props) {
super(props);
}
static navigationOptions = {
title: 'Login',
};
state = {
LoginEmail: "",
LoginPassword: "",
};
componentDidMount() {
this.watchAuthState(this.props.navigation)
try {
window = undefined;
} catch (e) {
}
}
watchAuthState(navigation) {
firebase.auth().onAuthStateChanged(function(user) {
console.log('onAuthStateChangedLOGIN: ', user)
if (user) {
// user.displayName will be like vendor.Peter
// e.g. role.name
var tempName = user.displayName;
navigation.navigate('Main', {
userRole: tempName.substr(0,tempName.indexOf('.'))
});
}
});
}
signIn(LoginEmail, LoginPassword) {
FirebaseAPI.signInUser(LoginEmail, LoginPassword);
}
render() {
return (
<Container style={styles.container}>
<Form>
<Text style={styles.text}>Login</Text>
<Item style={styles.standardDefaultInput} floatingLabel>
<Label style={{textAlign: 'center'}}>Email (example#example.com)</Label>
<Input
autoCapitalize="none"
style={{textAlign: 'center'}}
autoCorrect={false}
onChangeText={(text) => this.setState({LoginEmail: text})}
value={this.state.LoginEmail}
/>
</Item>
<Item style={styles.standardDefaultInput} floatingLabel>
<Label style={{textAlign: 'center'}}>Password (min. 6 charatcers)</Label>
<Input
autoCapitalize="none"
style={{textAlign: 'center'}}
autoCorrect={false}
onChangeText={(text) => this.setState({LoginPassword: text})}
value={this.state.Password}
/>
</Item>
<Button style={styles.standardDefaultButton} onPress={() => this.setState(this.signIn(this.state.LoginEmail, this.state.LoginPassword))} full rounded success>
<Text>Log In</Text>
</Button>
<Button style={styles.standardDefaultButton} onPress={() => this.props.navigation.navigate('SignUp')} full rounded link>
<Text>Sign Up</Text>
</Button>
</Form>
</Container>
);
};
}
Do take note that the navigation to Main is to the TabNavigator
From MainTabNavigator
let bottomTabNavigator = null
//let user = navigation.getParam(user)
//let userRole = user.displayName.substr(0,user.displayName.indexOf('.'))
//const { navigation } = this.props;
// The above failed
let userRole = navigation.getParam('userRole');
if (userRole == "vendor") {
bottomTabNavigator = createBottomTabNavigator({
HomeStack,
ListingStack,
CalendarStack,
ProfileStack
});
} else {
bottomTabNavigator = createBottomTabNavigator({
HomeStack,
CalendarStack,
ProfileStack
});
}
export default bottomTabNavigator
Use the global function to pass the value.
Global functions are useful when you need to pass data to a screen that is not a relationship between parents and children.
Global function Screen:
let NICKNAME = "";
function setNickName(data) {
NICKNAME = data;
}
function getNickName() {
return NICKNAME;
}
export {setNickName,getNickName }
senddata Screen:
import {setNickName} from "GlobalFunctionScreen"
...
this.state={
data: "sendData"
}
...
componentDidMount(){
setNickName(this.state.data);
}
receive data Screen:
import {getNickName} from "GlobalFunctionScreen"
...
componentDidMount(){
data = getNickName();
alert(data);
}

Redirect to another screen after login. (React Native)

So I edited this with just one file; everything is there but I still can't seem to make it work.
Directory Structure:
LoginScreen.js
import { StackNavigator, } from 'react-navigation';
import DrawerScreen from '../Containers/Drawer.js';
// PJDS all-in
import HomeScreen from '../screens/HomeScreen.js';
import SettingsScreen from '../screens/SettingsScreen.js';
import { DrawerNavigator } from 'react-navigation';
const Navigation=DrawerNavigator({
Dashboard: {
screen: HomeScreen
},
Course: {
screen: SettingsScreen
},
})
// PJDS end
class LoginScreen extends Component {
constructor(props){
super(props)
}
showLogin(props){
let { onLogin, onLogout, onUser, handleSubmit, auth } = props
if(auth.access_token === '') {
return (
<View >
<Field style={styles.input} autoCapitalize="none" placeholder="Email Cu" component={TInput} name={'email'} />
<Field style={styles.input} autoCapitalize="none" placeholder="Password" secureTextEntry={true} component={TInput} name={'password'} />
<Button
title = "Login"
color = "#236CF5"
style = {{backgroundColor:'#F8F8F8'}}
onPress = {handleSubmit(onLogin)}
/>
</View>
)
}
else {
return (
<Navigation />
)
}
}
render(){
return this.showLogin(this.props)
}
}
After I click login, only a blank screen will appear. There are no errors but when I swipe to the right, no drawer will show up.
What is the problem here?
The navigate variable has not been defined anywhere in your method.
Since you've already bound your screens to react-navigation, navigator, therefore you can get access to the props this.props.navigation which in turn has a method navigate.
let { {/* Other props */}, navigation } = props
...
onPress={()=> navigation.navigate('theDrawer')}
You should assign a width to your drawerNavigator
const Navigation = DrawerNavigator({
Home: {
screen: Home
},
Login: {
screen: LoginScreen,
},
}, {
drawerWidth: 300,
drawerBackgroundColor: '#00234b',
contentOptions: {
inactiveTintColor: '#FAFAFA',
activeTintColor: '#2bbfed',
activeBackgroundColor: '#00152d',
labelStyle: {
fontFamily: 'Montserrat-Medium'
}
},
});
To access your drawer navigator you should do this:
.....
onPress={() => this.props.navigation.navigate('DrawerToggle')}

Pass Data between Pages in React native

Im new to react native and I'm stuck at following.
Im performing navigation (when clicked on alert view button) using the code below.
const {navigation} = this.props.navigation;
…
.
.
{ text: 'Done', onPress:() => {
navigate.push(HomeScreen);}
How can I pass data to another Page in React native? Can I declare the parameter global and just assign to it?
What would be the correct way of performing this and how would I go about it?
Note
This answer was written for react-navigation: "3.3.0". As there are newer versions available, which could bring changes, you should make sure that you check with the actual documentation.
Passing data between pages in react-navigation is fairly straight forward. It is clearly explained in the documentation here
For completeness let's create a small app that allows us to navigate from one screen to another passing values between the screens. We will just be passing strings in this example but it would be possible to pass numbers, objects and arrays.
App.js
import React, {Component} from 'react';
import AppContainer from './MainNavigation';
export default class App extends React.Component {
render() {
return (
<AppContainer />
)
}
}
MainNavigation.js
import Screen1 from './Screen1';
import Screen2 from './Screen2';
import { createStackNavigator, createAppContainer } from 'react-navigation';
const screens = {
Screen1: {
screen: Screen1
},
Screen2: {
screen: Screen2
}
}
const config = {
headerMode: 'none',
initialRouteName: 'Screen1'
}
const MainNavigator = createStackNavigator(screens,config);
export default createAppContainer(MainNavigator);
Screen1.js and Screen2.js
import React, {Component} from 'react';
import { View, StyleSheet, Text, Button } from 'react-native';
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
}
});
Here we have 4 files. The App.js which we will import the MainNavigation.js. The MainNavigation.js sets up a StackNavigator with two screens, Screen1.js and Screen2.js. Screen1 has been set as the initial screen for our StackNavigator.
Navigating between screens
We can navigate from Screen1 to Screen2 by using
this.props.navigation.navigate('Screen2');
and we can go back to Screen1 from Screen2 by using
this.props.navigation.goBack();
So code in Screen1 becomes
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
<Button title={'Go to screen 2'} onPress={() => this.props.navigation.navigate('Screen2')} />
</View>
)
}
}
And code in Screen2 becomes:
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
<Button title={'Go back'} onPress={() => this.props.navigation.goBack()} />
</View>
)
}
}
Now we can navigate between Screen1 and Screen2
Sending values from Screen1 to Screen2
To send a value between Screen1 and Screen2, two steps are involved. First we have to send it, secondly we have to capture it.
We can send a value by passing it as a second parameter. Notice how the text value is contained in an object.
this.props.navigation.navigate('Screen2', {text: 'Hello from Screen 1' });
And we can capture it in Screen2 by doing the following, the first value in getParams is the key the second value is the default value.
const text = this.props.navigation.getParams('text','nothing sent');
So Screen1 now becomes
export default class Screen extends React.Component {
render() {
return (
<View style={styles.container}>
<Button
title={'Go to screen 2'}
onPress={() => this.props.navigation.navigate('Screen2', {
text: 'Hello from screen 1'
})} />
</View>
)
}
}
And code in Screen2 becomes:
export default class Screen extends React.Component {
render() {
const text = this.props.navigation.getParam('text', 'nothing sent')
return (
<View style={styles.container}>
<Text>{text}</Text>
<Button
title={'Go back'}
onPress={() => this.props.navigation.goBack()} />
</View>
)
}
}
Sending values from Screen2 back to Screen1
The easiest way I have discovered to send a value from Screen2 to Screen1 is to pass a function to Screen2 from Screen1 that will update the state in Screen1 with the value that you want to send
So we can update Screen1 to look like this. First we set an initial value in state. Then we create a function that will update the state. Then we pass that function as a parameter. We will display the captured value from Screen2 in a Text component.
export default class Screen1 extends React.Component {
state = {
value: ''
}
receivedValue = (value) => {
this.setState({value})
}
render() {
return (
<View style={styles.container}>
<Button
title={'Go to screen 2'}
onPress={() => this.props.navigation.navigate('Screen2', {
text: 'Hello from Screen 1',
receivedValue: this.receivedValue }
)} />
<Text>{this.state.value}</Text>
</View>
)
}
}
Notice that we are passing the function receivedValue in the same way that we passed the text earlier.
Now we have to capture the value in Screen2 and we do that in a very similar way that we did previously. We use getParam to get the value, remembering to set our default. Then when we press our Go back button we update it to call the receivedValue function first, passing in the text that we want to send back.
export default class Screen2 extends React.Component {
render () {
const text = this.props.navigation.getParam('text', 'nothing sent');
const receivedValue = this.props.navigation.getParam('receivedValue', () => {});
return (
<View style={styles.container}>
<Button
title={'Go back'}
onPress={() => {
receivedValue('Hello from screen 2')
this.props.navigation.goBack()
}} />
<Text>{text}</Text>
</View>
);
}
}
Alternatives to using getParam
It is possible to not use the getParam method and instead access the values directly. If we were to do that we would not have the option of setting a default value. However it can be done.
In Screen2 we could have done the following:
const text = this.props.navigation.state.params.text;
const receivedValue = this.props.navigation.state.params.receivedValue;
Capturing values in lifecycle events (Screen1 to Screen2)
react-navigation allows you to capture values using the lifecycle events. There are a couple of ways that we can do this. We could use NavigationEvents or we could use listeners set in the componentDidMount
Here is how to set it up using NavigationEvents
import React, {Component} from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { NavigationEvents } from 'react-navigation'; // you must import this
export default class Screen2 extends React.Component {
state = {
text: 'nothing passed'
}
willFocusAction = (payload) => {
let params = payload.state.params;
if (params && params.value) {
this.setState({value: params.value});
}
}
render() {
return (
<View style={styles.container}>
<NavigationEvents
onWillFocus={this.willFocusAction}
/>
<Text>Screen 2</Text>
<Text>{this.state.text}</Text>
</View>
)
}
}
Here is how to do it using listeners in the componentDidMount
export default class Screen2 extends React.Component {
componentDidMount () {
// we add the listener here
this.willFocusSubscription = this.props.navigation.addListener('willFocus', this.willFocusAction);
}
componentWillUmount () {
// we remove the listener here
this.willFocusSubscription.remove()
}
state = {
text: 'nothing passed'
}
willFocusAction = (payload) => {
let params = payload.state.params;
if (params && params.value) {
this.setState({value: params.value});
}
}
render() {
return (
<View style={styles.container}>
<Text>Screen 2</Text>
<Text>{this.state.text}</Text>
</View>
)
}
}
Passing navigation via components
In the above examples we have passed values from screen to screen. Sometimes we have a component on the screen and we may want to navigate from that. As long as the component is used within a screen that is part of a navigator then we can do it.
If we start from our initial template and construct two buttons. One will be a functional component the other a React component.
MyButton.js
// this is a functional component
import React, {Component} from 'react';
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
export const MyButton = ({navigation, value, title}) => {
return (
<TouchableOpacity onPress={() => navigation.navigate('Screen2', { value })}>
<View style={styles.buttonStyle}>
<Text>{title}</Text>
</View>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
buttonStyle: {
width: 200,
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'red'
}
});
MyOtherButton.js
// this is a React component
import React, {Component} from 'react';
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
export default class MyOtherButton extends React.Component {
render() {
const { navigation, value, title } = this.props;
return (
<TouchableOpacity onPress={() => navigation.navigate('Screen2', { value })}>
<View style={styles.buttonStyle}>
<Text>{title}</Text>
</View>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
buttonStyle: {
width: 200,
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'yellow'
}
});
Regardless of the type of component, notice that navigation is a prop. We must pass navigation to the component otherwise it will not work.
Screen1.js
import React, {Component} from 'react';
import { View, StyleSheet, Text, Button } from 'react-native';
import { MyButton } from './MyButton';
import MyOtherButton from './MyOtherButton';
export default class Screen1 extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Screen 1</Text>
<MyButton
title={'Press my button'}
navigation={this.props.navigation}
value={'this is a string passed using MyButton'}
/>
<MyOtherButton
title={'Press my other button'}
navigation={this.props.navigation}
value={'this is a string passed using MyOtherButton'}
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
}
});
Notice in Screen1.js as it is contained in a StackNavigator it will have access to this.props.navigation. We can pass that through to our component as a prop. As long as we use that in our component then we should be able to navigate by using the components own functionality.
<MyButton
title={'Press my button'}
navigation={this.props.navigation} // pass the navigation here
value={'this is a string passed using MyButton'}
/>
Snacks
Here is a snack for passing params.
Here is a snack for passing params and capturing in lifecycle events.
Here is a snack passing navigation to components
1) On Home Screen:-
Initialise:-
constructor(props) {
super(props);
this.navigate = this.props.navigation.navigate; }
Send:-
this.navigate("DetailScreen", {
name: "Detail Screen",
about:"This is Details Screen Page"
});
2) On Detail Screen:-
Initialise:-
constructor(props) {
super(props);
this.params = this.props.navigation.state.params;
}
Retrive data:-
console.log(this.params.name);
console.log(this.params.about);
const {navigate} = this.props.navigation;
…
.
.
{ text: 'Done', onPress:() => {
navigate('homeScreen',...params);}
You can get those params like
const {params} = this.props.navigation.state
HomeScreen.js
this.props.navigation.navigate('Screen2',{ user_name: 'aaa',room_id:'100' });
Screen2.js
const params = this.props.route.params;
user_name = params.user_name;
room_id = params.room_id
You can easily send and receive your params with react-navigation like below
Send params:
{
text: 'Done',
onPress: () => {
this.props.navigation.navigate(
HomeScreen,
{param1: 'value1', param2: 'value2'}
);
}
}
Get params in HomeScreen:
const { navigation } = this.props;
var param1 = navigation.getParam('param1', 'NO-VALUE');
var param2 = navigation.getParam('param2', 'NO-VALUE');
the 'NO-VALUE' is default value, if there is not desired param
I am assuming that you are using react-navigation. So, in react-navigation we can pass data in two pieces:
Pass params to a route by putting them in an object as a second parameter to the navigation.navigate function:
this.props.navigation.navigate('RouteName', { /* params go here */ })
Read the params in your screen component:
this.props.navigation.getParam(paramName, someDefaultValue)
Alert Button
<Button
title="Alert View"
onPress={() => {
this.props.navigation.navigate('alerts', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
Screen:
const itemId = navigation.getParam('itemId', 'NO-ID');
const otherParam = navigation.getParam('otherParam', 'some default value')
Screen 1:
<Button title="Go Next"
onPress={() => navigation.navigate('SecondPage', { paramKey: userName })} />
Screen 2:
const SecondPage = ({route}) => {
....
....
<Text style={styles.textStyle}>
Values passed from First page: {route.params.paramKey}
</Text>
....
....
}

StackNavigator through Component gives undefined error

I was trying to use StackNavigator for navigation and it works when I use it to go from one screen to the other as explained here. But when I try to have a subcomponent to navigate through itself, the navigation doesn't seem to work and I couldn't find any solution to it.
As given in the code below, I'm trying to use the Test Component in which there is a button that can be clicked to move from HomeScreen to ChatScreen.
I'm pretty sure the solution is something basic, but I really can't find it anywhere.
Here's my code:
import React from 'react';
import {
AppRegistry,
Text,
View,
Button
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
let userName = 'Ketan';
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat', { user: userName })}
title={"Chat with " + userName}
/>
<Test />
</View>
);
}
}
class ChatScreen extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: `Chat with ${navigation.state.params.user}`,
});
render() {
const { params } = this.props.navigation.state;
return (
<View>
<Text>Chat with {params.user}</Text>
</View>
);
}
}
class Test extends React.Component {
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Button
onPress={() => navigate('Chat', { user: 'TestBot' })}
title={'This is a test'}
/>
</View>
)
}
}
const NavApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen },
});
AppRegistry.registerComponent('NavApp', () => NavApp);
Here's the error I'm getting:
Here's the demo to test: https://snack.expo.io/HyaT8qYob
I hope my question is clear enough of what I mean.
Since your Test component does not belong to navigation stack it doesn't have the navigation prop. You can do couple of things.
Simple one is to pass the navigation to the child component like the example below.
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat', { user: userName })}
title={"Chat with " + userName}
/>
<Test navigation={this.props.navigation} />
</View>
);
The second option is, you can use withNavigation from react-navigation. You can find more details about it here
import { Button } 'react-native';
import { withNavigation } from 'react-navigation';
const MyComponent = ({ to, navigation }) => (
<Button title={`navigate to ${to}`} onPress={() => navigation.navigate(to)} />
);
const MyComponentWithNavigation = withNavigation(MyComponent)
withNavigation
withNavigation is a higher order component which passes the
navigation prop into a wrapped component. It's useful when you
cannot pass the navigation prop into the component directly, or
don't want to pass it in case of a deeply nested child.