I have two navigators: AppNavigatorand BottomBarNavigator, the first one is for the authentication flow, the second one is when the user is authenticated.
These navigators are inside a createSwitchNavigator, and I want the right navigator to be chosen based on a loggedIn variable I store inside my store/auth.
_Router.js_
const MyNavigator = (!loggedIn) ? createSwitchNavigator({
AppNavigator: AppNavigator,
BottomBarNavigator: BottomBarNavigator,
}) : createSwitchNavigator({
BottomBarNavigator: BottomBarNavigator,
AppNavigator: AppNavigator,
});
I persist my store/auth variable inside redux-persist.
_Store.js_
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['auth']
};
const pReducer = persistReducer(persistConfig, reducers);
export const store = createStore(pReducer, undefined, applyMiddleware(ReduxThunk, logger));
export const persistor = persistStore(store);
In my App.js, I wrap my navigator with PersistGate, but although the state is rehydrated after some milliseconds, the Navigator is rendered before and so is always chosen the AppNavigator.
_App.js_
import { PersistGate } from 'redux-persist/es/integration/react'
render(){
return (
<Provider store={store}>
<PersistGate
loading={null}
onBeforeLift={this.onBeforeLift}
persistor={persistor}>
<MyNavigator ref={navigatorRef => {NavigationService.setTopLevelNavigator(navigatorRef);}}/>
</PersistGate>
</Provider>
Any suggestions to make it work properly?
You might want to have some kind of "loading"-scene as part of loading=. Did you try using anything else than null?
import React from 'react';
import { View, ActivityIndicator, SafeAreaView } from 'react-native';
export class ApplicationLoadingScreen extends React.Component {
render() {
return (
<View style={{flex: 1, justifyContent: 'center'}}>
<ActivityIndicator size="large" color="#000000" />
</View>
);
}
}
We are using react-navigation and are managing that "already logged in"-switch as part of the onBeforeLift-method. As the first scene inside a SwitchNavigator-stack we included that same loading-scene as initialRouteName. This makes the app starting with the loading-scene (shown through the PersistGate), then being shown as part of the SwitchNavigator-stack, then switched to the login-screen or the "inside"-part afterwards.
Maybe this helps a bit.
inside
Thank you for the answer.
Unfortunately I already tried to use a view as the loading view, but nothing change.
How do yo change afterwards?
Unlike you showed above, we just have only one navigator for this. Even react-navigation says that "dynamic navigation" is a bit flacky, therefor we are using a switch-navigator, and switch scenes after store was rehydrated. Here some example how we manage this:
import React from 'react';
// ....
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import { store, persistor } from './redux/store';
const AppNavigationContainer = createAppContainer(
createSwitchNavigator({
ApplicationLoadingScreen: {
screen: ApplicationLoadingScreen
},
UnauthedScreen: {
screen: LoginStack // to be defined elsewhere
},
AuthedScreen: {
screen: InsideStack // to be defined elsewhere
}
}, {
initialRouteName: 'ApplicationLoadingScreen'
})
);
export default class App extends React.Component {
/**
* registers the passed reference into the internal handle-instance, its not stored
* as part of the redux-state, so we do not have to wait for re-hydration procedure
*/
registerNavigator = ref => {
store.dispatch({
type: 'REGISTER_SCENE_NAVIGATOR_HANDLE',
payload: ref
});
}
runInitialAppStartActions = () => {
// store now has been rehydrated
// check inside store if we have some auth token as marker for the user being "logged in"
if (!!store.getState().auth.token) {
// TODO do something to check that token being still valid
store.dispatch({
type: 'NAVIGATE_TO_SCENE',
payload: 'AuthedScreen'
});
} else {
// we now know that user is not yet logged in, so switch to login-screen
store.dispatch({
type: 'NAVIGATE_TO_SCENE',
payload: 'UnauthedScreen'
});
}
}
render() {
return (
<Provider store={store}>
<PersistGate loading={<ApplicationLoadingScreen />} persistor={persistor} onBeforeLift={this.runInitialAppStartActions}>
<AppNavigationContainer ref={navigatorRef => { this.registerNavigator(navigatorRef); }}/>
</PersistGate>
</Provider>
);
}
}
Please keep in mind, that we have a way to navigate via redux-dispatch actions.
Hi @aboscus,
we do have similar problem and after debugging I found that this workaround (passing children as a function) works.
The problem is, that React "executes the children" despite the fact that there is the a condition in the render method of PersistGate.
render() {
return (
<Provider
store={store}
children={bootstrapped => {
if (!bootstrapped) {
return null
}
return (
<PersistGate
loading={null}
onBeforeLift={this.onBeforeLift}
persistor={persistor}
>
<MyNavigator
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef)
}}
/>
</PersistGate>
)
}}
/>
)
}
Adding empty function seems to fix the issue for me.
const onBeforeLift = () => ({});
...
<PersistGate loading={loadingComponent} persistor={persistor} onBeforeLift={onBeforeLift}>
Most helpful comment
Unlike you showed above, we just have only one navigator for this. Even react-navigation says that "dynamic navigation" is a bit flacky, therefor we are using a switch-navigator, and switch scenes after store was rehydrated. Here some example how we manage this:
Please keep in mind, that we have a way to navigate via redux-dispatch actions.