How to config app react-native-navigation v2 with redux-persist?
This is my attempt, don't know if it is recommended:
const store = configureStore();
const persistor = persistStore(store);
const createApp = (Component, ...props) => {
return class App extends React.Component<TProps> {
render() {
return (
<Provider store={store}>
<PersistGate
loading={<Spinner size="large" />}
persistor={persistor}
>
<Component {...{
...this.props,
...props,
}} />
</PersistGate>
</Provider>
);
}
}
}
export const registerScreens = () => {
Navigation.registerComponent('app.Login', () => createApp(LoginScreen));
Navigation.registerComponent('app.Dashboard', () => createApp(DashboardScreen));
}
Thank you for helping but it still not working!
i was config router.js the same your code and two file has folow content:
===== app.js ===
import { registerScreens } from 'rnapp/App/Router';
import { Navigation } from 'react-native-navigation';
registerScreens();
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
component: {
name: 'app.Home'
}
}
});
});
===== index.js ===
import { AppRegistry } from 'react-native';
import App from './app';
AppRegistry.registerComponent('rnapp', () => App);
==== Error ======
Please help me, thank you!
First, you should replace your index.js content with the one from app.js. registerAppLaunchedListener
should be called instead of registerComponent
.
Second, probably you are not exporting properly your screen component. For example, try to console.log(YourScreen)
after you import it to check it's a function.
Yes! I tried this method, and this is app's error
Oh thank you Pro: gabrielhpugliese. I resolved this error, it's exist in AppDelegate.m because i config not correct.
Keep in mind that I think this solution is not performant because I think it creates a Provider for each screen, but I do not have a better solution. Take a look on this one: https://github.com/wix/react-native-navigation/issues/1642
Yes i will find better solution, if found, i will share it in this issue. Thank you!
You can pass 3rd parameter to persistStore which is callback that's triggered when store is rehydrated. So I simply call Navigation.setRoot after that.
...
import { persistStore as persistStoreRaw } from 'redux-persist';
...
const store = configureStore();
// promisify persistStore
const persistStore = (store: any) =>
new Promise(resolve => {
persistStoreRaw(store, undefined, () => {
resolve();
});
});
// promisify app launched event
const onAppLaunched = () =>
new Promise(resolve => {
Navigation.events().registerAppLaunchedListener(() => {
resolve();
});
});
(async () => {
registerScreens(store);
// persist store and w8 for app start simultaneously
await Promise.all([persistStore(store), onAppLaunched()]);
Navigation.setDefaultOptions({...});
await Navigation.setRoot({
root: {...}
});
})();
I wrap each screen with hoc containing <Provider store={store} />
. Because I wait for store rehydratation before I call Navigation.setRoot
I don't have to use <PersistGate />
. Also don't forget to use hoist-non-react-statics
otherwise your static get options()
on screens won't work.
Thank you for helping Zycon42!
But it still not working and require
@gabrielhpugliese I saw in the docs for V2 you should register the screens but with a different function:
Navigation.registerComponentWithRedux('com.app.SearchScreen', () => Search, Provider, store);
Thanks you alexxsanchezm!
May be this is new feature updated, i will try again.
When calling persistStore like this, the component does not render but when i remove the persistStore function it works
`import { Navigation } from 'react-native-navigation';
import { persistStore } from 'redux-persist';
import { registerScreens } from './src/screens/Screen';
import store from './src/store';
registerScreens();
persistStore(store, null, () => {
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'BetGold.OddsScreen'
}
}
],
options: {
topBar: {
title: {
text: 'Blog'
}
}
}
}
}
})
})
})
`
@Zycon42 persistStore promise is never fulfilled for me on iOs.
Can any one share source code ?
Im looking for RNN v2 and RN >=0.56 intit project. but it all error :(
How does registerComponentWithRedux react to store changes ?
Since store is immutable so store ref. Will change , does changes that occur in one root will propagate to other roots ?
any full example please ?
@gabrielhpugliese I saw in the docs for V2 you should register the screens but with a different function:
Navigation.registerComponentWithRedux('com.app.SearchScreen', () => Search, Provider, store);
@alexxsanchezm Thanks man...!
Closing as all solutions provided are not complete.
The listener for RNN's start up event must always be listening. This is a better solution.
import { Navigation } from 'react-native-navigation';
import { persistStore as persistStoreRaw } from 'redux-persist';
import store from './src/store/store';
import { registerScreens } from './src/screens/screens';
import { setInitialLayout } from './src/NavigationController';
/**
* Wait till our store is persisted
* @param {store} storeToPersist - The redux store to persist
* @returns {Promise} - Promise that resolves when the store is rehydrated
*/
const persistStore = storeToPersist => new Promise((resolve) => {
persistStoreRaw(storeToPersist, undefined, () => {
resolve();
});
});
/**
* We register screens
* then we wait for
* - Store to be rehydrated
* and then we finally initialize
* layout accordingly.
*/
async function bootstrap() {
registerScreens(store);
// Add any more promises that must be resolved before layout is set
await Promise.all([persistStore(store)]);
setInitialLayout();
}
/**
* The initial listener of our app,
* this will get triggered on app start or when
* the Android activity is recreated.
* (For example by pressing back button on the
* root screen)
*/
Navigation.events().registerAppLaunchedListener(() => {
bootstrap();
});
No need to do anything fancy with promises
simply create store as described in redux-persist docs
then in index.js file add a wrapper function on every screen like this.
/** @Format */
// import {AppRegistry} from 'react-native';
// import App from './App';
// import {name as appName} from './app.json';
// AppRegistry.registerComponent(appName, () => App);
// //
import {Navigation} from "react-native-navigation";
import themeConstants from "./src/theme";
import {componentIds, screenNames} from "./src/screens";
import { PersistGate } from 'redux-persist/integration/react'
import TodoList from "./src/screens/ToDo/ToDo";
import CommentsScreen from "./src/screens/ToDo/commentsScreen";
import {persistStore} from "redux-persist";
import {Provider} from "react-redux";
import store from "./src/store/createStore/createStore";
import React from "react";
import {AsyncStorage} from "react-native";
import {Platform, ActivityIndicator} from "react-native";
//let persistor = persistStore(store().store)
const TodoListWithStore = (props) => {
return (
);
};
const Loading = () => {
return (
);
};
const CommentsScreenWithStore = (props) => {
return (
<Provider store={store().store}>
<PersistGate loading={Loading()} persistor={store().persistor}>
<CommentsScreen {...props}/>
</PersistGate>
</Provider>
);
};
Navigation.registerComponentWithRedux(screenNames.toDo, () => TodoListWithStore, Provider, store().store);
Navigation.registerComponentWithRedux(screenNames.commentsScreen, () => CommentsScreenWithStore, Provider, store().store);
/* topBar: {
visible: true,
animate: true,
hideOnScroll: true,
buttonColor: themeConstants.pink800,
backButton: {
color: themeConstants.pink400
},
background: {
color: themeConstants.primary
},
title: {
color: themeConstants.offWhite,
fontSize: 18,
alignment: "center"
}
*/
Navigation.setDefaultOptions({
topBar: {
title: {
...Platform.select({
ios: { fontFamily: 'System', },
android: { fontFamily: 'Roboto' }
}),
color: '#262626',
alignment: 'center',
},
alignment: 'center',
elevation: 0,
noBorder: true,
drawBehind: true,
background: {
color: 'transparent'
}
},
statusBar: {
visible: true,
style: "light",
backgroundColor: themeConstants.primary
},
layout: {
backgroundColor: themeConstants.light,
orientation: ["portrait"] //["portrait", "landscape"] An array of supported orientations
},
bottomTabs: {
visible: true,
animate: true, // Controls whether BottomTabs visibility changes should be animated
backgroundColor: themeConstants.secondary
},
bottomTab: {
iconColor: themeConstants.tertiary,
selectedIconColor: themeConstants.pink400,
textColor: themeConstants.tertiary,
selectedTextColor: themeConstants.pink400,
fontFamily: "Helvetica",
fontSize: 10
}
});
const stack = {
children: [
{
component: {
id: componentIds.toDoStack,
name: screenNames.toDo,
passProps: {}
}
}
],
options: {}
};
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
stack: stack
}
});
});
on android, if store is not updaed on some screen then call Persistor.persist() method in that component with some if condition ... these two simple steps worked for me
Yup, agreed with the no need for fancy promises.
Following these steps (initially for nav v1), I had it adapted for v2 by putting _persistStore_ before my registerAppLaunchedListener, which was working fine on iOS, but had just a blank screen on Android.
I simply moved the _persistStore_ inside the registerAppLaunchedListener, and that did the trick for me
Navigation.events().registerAppLaunchedListener(() => {
persistStore(store, null, () => {
...
})
})
This is my attempt, don't know if it is recommended:
const store = configureStore(); const persistor = persistStore(store); const createApp = (Component, ...props) => { return class App extends React.Component<TProps> { render() { return ( <Provider store={store}> <PersistGate loading={<Spinner size="large" />} persistor={persistor} > <Component {...{ ...this.props, ...props, }} /> </PersistGate> </Provider> ); } } } export const registerScreens = () => { Navigation.registerComponent('app.Login', () => createApp(LoginScreen)); Navigation.registerComponent('app.Dashboard', () => createApp(DashboardScreen)); }
Looks good initially, but be aware that this wraps every single screen by itself and is thus executed every time a screen is called.
@gabrielhpugliese I saw in the docs for V2 you should register the screens but with a different function:
Navigation.registerComponentWithRedux('com.app.SearchScreen', () => Search, Provider, store);
This is deprecated now. Should be used like this now:
Navigation.registerComponent(
"Authentication",
() => props => (
<Provider store={store}>
<Authentication {...props} />
</Provider>
),
() => Authentication
);
Every component needs to be wrapped with all the providers? So We will have a lot redundant provider instances in Memory, right?
@nbstr
Every component needs to be wrapped with all the providers? So We will have a lot redundant provider instances in Memory, right?
@nbstr
I think this is not quite true, because RNN render just the current view that you are watching, so, when you push to another view, that view will get unmounted and then the new view will get mounted, that's why shouldn't have some memory issues about this.
Most helpful comment
This is my attempt, don't know if it is recommended: