Logging User after Persist Rehydration - react-native

I am trying to login user after Rehydration in react-native. I am not sure how to go about redirecting user after I have data in the store.
Here is how I setup my PersistGate in my index.js file:
class AppRedux extends React.Component {
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor} loading={<SplashScreen />}>
<App />
</PersistGate>
</Provider>
);
}
}
AppRegistry.registerComponent(appName, () => AppRedux);
SplashScreen.js:
class SplashScreen extends Component {
constructor(props) {
super(props);
props.navigation.navigate(RouteNames.Home);
}
render() {
return <View style={{ flex: 1, backgroundColor: "green" }}></View>;
}
}
const mapStateToProps = state => {
return {
rehydrated: state.user.rehydrated
};
};
export default connect(mapStateToProps)(SplashScreen);
The problem with this approach is that I am not able to navigate from splash screen. I get Navigation prop is undefined.

"loading" prop in "PersistGate" is just a dummy component to show loader until Persist Rehydration . "App"(navigation component) will load after Rehydration where you can receive "navigation" prop and can access store.
Try this like described in official site Auth Flow
Create AuthLoading Screen and set navigation from there
App.js
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createAppContainer(
createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
)
);

Related

How to nest a component wrapping a stack navigator inside a tab navigator react

Current Behavior
I am trying to have a tab navigator, where one of the screens/tabs has a component, that, among other stuff, has a stack navigator in it.
However, I currently get a 'No "routes" found in navigation state' error.
How to reproduce
The code I'm currently running can also be found as a snack.
Code:
import * as React from 'react';
import { Button, Text, View, StyleSheet } from 'react-native';
import { createAppContainer, createStackNavigator, createBottomTabNavigator } from 'react-navigation';
class ScreenA extends React.Component {
static navigationOptions = {
tabBarLabel: 'A',
};
render() {
return (
<View>
<Text>Screen A</Text>
</View>
);
}
}
class SettingsHome extends React.Component {
render() {
return (
<Button onPress={() => this.props.navigation.navigate('SettingsScreenA')}>
<Text>Navigate to Settings A</Text>
</Button>
);
}
}
class SettingsScreenA extends React.Component {
render() {
return (
<View>
<Text>Settings A</Text>
<Button onPress={() => this.props.navigation.navigate('SettingsA')}>
<Text>Back to SettingsHome</Text>
</Button>
</View>
);
}
}
const SettingsStackNavigator = createStackNavigator({
SettingsHome: { screen: SettingsHome },
SettingsScreenA: { screen: SettingsScreenA }
})
class SettingsScreen extends React.Component {
static navigationOptions = {
tabBarLabel: 'Settings',
}
render() {
return (
<View>
<Text>Some other components...</Text>
<SettingsStackNavigator navigation={this.props.navigation}/>
</View>
);
}
}
const RootTabNavigator = createBottomTabNavigator({
ScreenA: { screen: ScreenA },
Settings: {screen: SettingsScreen },
});
const Navigation = createAppContainer(RootTabNavigator);
export default class App extends React.Component {
render() {
return (
<Navigation />
);
}
}
in this button you want to navigate to SettingsA
<Button onPress={() => this.props.navigation.navigate('SettingsA')}>
<Text>Back to SettingsHome</Text>
</Button>
but you defined the route of Settings screen as Settings
const RootTabNavigator = createBottomTabNavigator({
ScreenA: { screen: ScreenA },
Settings: {screen: SettingsScreen },
});
you have to fix it by change the SettingsA as route name in tab navigator
const RootTabNavigator = createBottomTabNavigator({
ScreenA: { screen: ScreenA },
SettingsA: {screen: SettingsScreen },
});

How to redirect to Dashboard Screen after login successfully in react native expo?

When I placed LoginScreen in Tabbed Screens then I can successfully login and navigate to DashboardScreen. But I can't navigate to tabbed screen when calling from outside the tabbed screen.? Please help as soon as possible.
I would advise you to split your actual app navigator and the authentication navigator and create a simple "Auth flow" using a switch navigator like the following:
Login Screen example:
class SignInScreen extends React.Component {
render() {
return (
<View style={styles.container}>
<Button title="Sign in!" onPress={this._signInAsync} />
</View>
);
}
_signInAsync = async () => {
await AsyncStorage.setItem('userToken', 'abc');
this.props.navigation.navigate('AuthLoading') // after you sign in navigate to the auth screen
};
}
Auth loading screen (Create this one):
class AuthLoadingScreen extends React.Component {
constructor() {
super();
this._bootstrapAsync();
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('userToken');
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
this.props.navigation.navigate(userToken ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
return (
<View style={styles.container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
and finally on your App.js:
import { createStackNavigator, createSwitchNavigator, createAppContainer } from 'react-navigation';
// you can use tab navigation or stack navigation in the AppStack
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createAppContainer(createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
));

React-Navigation: Navigation onPress doesn't work

I am a bit confused about this navigation system. I have root which I contain navigations in it. And I have two different screens. No problem till here. I put initialRoute and displaying SignInScreen. Also put a button for Sign Up screen but that button isn't working.
Root.js
const ScreenNavigator = createStackNavigator({
SignIn: SignInScreen,
SignUp: SignUpScreen,
},
{initialRouteName: 'SignIn' }
);
const ScreenContainer = createAppContainer(ScreenNavigator);
export default class Root extends Component{
render(){
return(
<ScreenContainer style={styles.container} />
);
}
}
and SignInScreen:
import SignUpScreen from '../screens/Signup';
export default class SignInScreen extends Component{
constructor(props){
super(props);
}
render(){
return(
<View style={styles.container}>
<Text>Sign in Screen </Text>
<Button
title={'Sign up'}
onPress={() => this.props.navigation.navigate('SignUpScreen')}
/>
</View>
);
}
}
is it something that I am missing here? Thank you for help.
There is nothing wrong in the code, you just miss spell the route.
There is no route name SignUpScreen, correct route name is SignUp and SignIn
confusion is due to change in api of react navigation
before we define a route like
Login: {
screen: LoginScreen
}
so login is a route name and screen is just a component we need to render
but after 2^ version, it changed to
{SignIn: SignInScreen}
so now name is SignIn and component is SignInScreen
even if you want a route name as component just do
const ScreenNavigator = createStackNavigator({
SignInScreen,
SignUpScreen,
},
now you route name is SignInScreen and SignUpScreen
hope this information is enough
Issue in route name.
onPress = {() => this.props.navigation.navigate('SignUp')}
Try to keep the route names in constant file. It may make things easier to handle.
const routes = {
route1: 'route1',
route2: 'route2',
};
const ScreenNavigator = createStackNavigator({
[routes.route1]: Screen1,
[routes.route2]: Screen2,
},
{initialRouteName: routes.route1 }
);

React native set state from drawer navigator to other component

On first screen I have a component having some listing of products say ProductListing.js. And on drawer navigator I have some check boxes. I want to set state of ProductListing.js when I am clicking on any check box of navigator.
App.js
import React, {Component} from 'react';
import Router from './src/config/routes';
export default class App extends React.Component {
render () {
return (
<Router/>
);
}
}
Router.js
export default DrawerNavigator({
Dashboard: {screen: Dashboard},
CreateLogin: {screen: CreateLogin},
Test: {screen: Test}
}, {
contentComponent: SideMenu,
drawerWidth: 300,
drawerPosition: 'right'
});
SideMenu.js
render () {
const { data, searchTerm, searchAttribute, ignoreCase, checked } = this.state;
return (
<View style={styles.container}>
<ScrollView>
<View>
<TextInput
style={styles.search} placeholder={"Search"}
onChangeText={searchTerm => this.setState({ searchTerm })} />
<SearchableFlatList
data={data} searchTerm={searchTerm}
searchAttribute={searchAttribute} ignoreCase={ignoreCase}
renderItem={({ item, index }) => <CheckBox
title={item.name +' ('+ item.count+')'}
onPress={() => this.handleChange(item.id)}
checked={checked[item.id]} />
}
keyExtractor={({id}, index) => index.toString()} />
</View>
</ScrollView>
<View style={styles.footerContainer}>
<Text>Apply filter by choosing filter values</Text>
</View>
</View>
);
}
}
ProductListing.js
constructor(props){
super(props);
this.state ={ isLoading: true,isloadmore: false, page :1,dataSource: [],countryFilter:0, gradesFilter:'',country:'',totalRecord:0};
}
render(){
const { navigation } = this.props;
return(
<View style={{flexDirection:'column',paddingRight:8}}>
<Button
title='Filter'
buttonStyle={{backgroundColor:'#000000',borderRadius:2}}
onPress={() => navigation.dispatch(DrawerActions.openDrawer())}
/>
</View>
);
}
Now on click of handleChange in SideMenu.js I want to update state of gradesFilter in ProductListing.js where I am updating my product listing.
You can easily achieve this with a portal! Take a look here.
You can pass the parameters from drawer to the Product screen via navigation params.
For this, you need to stackNavigator inside your DrawerNavigator like:
Router:
const MyStack = StackNavigator({
Dashboard: {screen: Dashboard},
CreateLogin: {screen: CreateLogin},
Test: {screen: Test}
});
const MyDrawer = DrawerNavigator({
Main: { screen: MyStack }
},
{
contentComponent: SideMenu,
drawerWidth: 300,
drawerPosition: 'right'
});
export const Router = StackNavigator({
Login: {screen: Login},
Drawer: {
screen: MyDrawer,
navigationOptions: { header: null } } //prevent double header
});
Now you can navigate and pass parameter from login or Dashboard or anyother screen via Drawer like
_login() {
this.props.navigation.navigate('Drawer', { parameter: userName });
}
And to use this parameter you need to access:
const { parameter } = this.props.navigation.state.params;
which you can further use to set state like
this.setState({
productSelection: parameter)};
Another approach can be via, listener/ Callback handler.

Passing props from a root component through a BottomTabNavigator

My app root component looks like this:
export default class App extends React.Component {
render() {
<RootTabs doThings={this.doThings} />
}
}
The RootTabs component is created by createBottomTabNavigator from react-navigation:
const RootTabs = createBottomTabNavigator({
Movies: {
screen: Movies
},
Actors: ... // etc
})
My problem is, I would like to transmit data (as a prop if possible) from the root component (App) to the Movies component, but Movies do not receive the doThings prop.
How to transmit props through a BottomTabNavigator to children?
If that's not possible, what would be the most simple way for a children component to call a method on a root component, when they are separated by a BottomTabNavigator?
Try using screenProps
screenProps is documented on this page
Answered referred from here
Minimal Example would be
import React, { Component } from 'react'
import { AppRegistry, Button, Text, View } from 'react-native'
import { StackNavigator } from 'react-navigation'
class HomeScreen extends Component {
render() {
const { navigation, screenProps } = this.props
return (
<View>
<Text>Welcome, {screenProps.user.name}!</Text>
<Button onPress={() => navigation.navigate('Profile')} title="Go to Profile" />
</View>
)
}
}
class ProfileScreen extends Component {
render() {
const { navigation, screenProps } = this.props
return (
<View>
<Text>My Profile</Text>
<Text>Name: {screenProps.user.name}</Text>
<Text>Username: {screenProps.user.username}</Text>
<Text>Email: {screenProps.user.email}</Text>
<Button onPress={() => navigation.goBack()} title="Back to Home" />
</View>
)
}
}
const AppNavigator = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
})
class MyApp extends Component {
render() {
const screenProps = {
user: {
name: 'John Doe',
username: 'johndoe123',
email: 'john#doe.com',
},
}
return (
<AppNavigator screenProps={screenProps} />
)
}
}
export default MyApp
AppRegistry.registerComponent('MyApp', () => MyApp);
HomeScreen and ProfileScreen are components defined as screens for AppNavigator.
In the example above, I am passing the user data from the top-level, root component MyApp to both HomeScreen and ProfileScreen.
Since there is a AppNavigator between MyApp and the screen components, we will need to pass the user to screenProps prop of AppNavigator, so that the AppNavigator will pass it down to the screens. Any other prop except screenProps will not be passed down.
MyApp <-- user data here .
AppNavigator <-- the StackNavigator, the middle man. must use screenProps to pass user data down .
HomeScreen <-- will receive user data from this.props.screenProps.user instead of this.props.user .
ProfileScreen <-- same as HomeScreen