How to persist redux store in React Native with redux-persist - react-native

I am using react-native with redux-persist and I cannot seem to get my store to persist into async storage. According to the redux-persist github repo, this may have to do with AsyncStorage being removed from react-native, but I cannot seem to figure out what needs to be done to fix the issue.
When my app launches I see the actions persist/PERSIST and persist/REHYDRATE fire in my redux devtools, but nothing ever is stored in async storage.
I thought I had set up my file as the redux-persist docs explained but I imagine I am missing something. I'm fairly new to Redux as it is so I may just be confused on something basic.
My Redux.config.js file ...
import React from 'react';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux'
import thunk from 'redux-thunk';
import { initialState } from './initialState';
import { puppyReducer } from './reducers/puppies/puppyReducer';
import { uiReducer } from './reducers/ui/uiReducer';
import { userReducer } from './reducers/user/userReducer';
import { contentReducer } from './reducers/content/contentReducer';
import { persistStore, persistReducer, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/lib/storage'
import logger from 'redux-logger';
import Navigation from './Navigation.config';
import { PersistGate } from 'redux-persist/integration/react';
const persistConfig = {
key: 'root',
storage: storage
}
const rootReducer = {
dogs: puppyReducer,
ui: uiReducer,
user: userReducer,
content: contentReducer
};
const reducer = persistCombineReducers(persistConfig, rootReducer);
const persistedReducer = persistReducer(persistConfig, reducer);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(persistedReducer, initialState, composeEnhancers(applyMiddleware(thunk), applyMiddleware(logger)));
const persistor = persistStore(store);
// Wrap the redux State Provider around the Main Navigation Stack
function StateWrapper() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Navigation />
</PersistGate>
</Provider >
)
}
export default App = StateWrapper;
I expect that this should load my persisted state when the app launches, and continue to update that persisted state as my redux state changes. As it stands currently, my state is updating fine, but nothing is persisted. There are no error messages showing.

I persist my redux store using AsyncStorage. Rather than using redux-persist's storage option, I have installed AsyncStorage from the #react-native-community. Just make sure that you link the dependency properly.
Then in my config.js file I import and use AsyncStorage as follows.
import AsyncStorage from '#react-native-community/async-storage';
const config = {
key: 'primary',
keyPrefix: '', // the redux-persist default `persist:` doesn't work with some file systems
storage: AsyncStorage,
};
Your issue could also be related to the keyPrefix try setting it to some value other than the default.

Try the following:
const reducer = persistCombineReducers(persistConfig, rootReducer);
const persistedReducer = persistReducer(persistConfig, reducer rootReducer);

This works for me:
//...
import {combineReducers} from 'redux';
import {persistReducer} from 'redux-persist';
const persistConfig = {
key: 'root',
storage: storage
}
const rootReducer = {
dogs: puppyReducer,
ui: uiReducer,
user: userReducer,
content: contentReducer
};
const persistedReducer = persistReducer(
persistConfig,
combineReducers(rootReducers)
);
//...

Related

Redux-persist not saving all the data in Expo/React Native app

I am creating an app with Expo/React Native with Redux. I want to persist data. But all of data is not saving on AsyncStorage. There are 3 keys in store and 2 of them are array and one of them is string. String one is saving but arrays not. You can find all my code in https://github.com/yunisdev/walllet . Can you please help me to fix this problem?
You are using Expo, so you should use the ExpoFileSystemStorage in order to store the state.
Like this:
import { AnyAction, applyMiddleware, combineReducers, createStore, Store } from "redux";
import ExpoFileSystemStorage from 'redux-persist-expo-filesystem';
import { persistStore, persistReducer } from 'redux-persist';
import thunk from 'redux-thunk';
const persistConfig = {
key: 'root',
storage: ExpoFileSystemStorage ,
whitelist: ['main']
};
const mainConfig = {
key: 'main',
storage: ExpoFileSystemStorage
}
import mainReducer from './reducer'
const rootReducer = combineReducers({
main: persistReducer(mainConfig, mainReducer)
})
const store: Store<any, AnyAction> = createStore(persistReducer(persistConfig, rootReducer), applyMiddleware(thunk))
const persistor = persistStore(store)
export {
store,
persistor
}

Redux-persist not persisting with expo on reload

I am using following tech stack
react-native
expo
redux
redux-persist.
I have tried articles, docs as well as questions here also but not help. Its simply not persisting. I am using emulator and running through expo only. As soon i close the app and reload expo client it simply doesn't persist and ask me to login again.
I also tried using AsyncStorage but still not working.
Hers's my code:
index.js
import thunk from "redux-thunk";
import AsyncStorage from "#react-native-community/async-storage";
import ExpoFileSystemStorage from "redux-persist-expo-filesystem"
// import storage from 'redux-persist/lib/storage';
import { createStore, applyMiddleware, compose } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import rootReducer from "./reducers/rootReducer";
import logger from "redux-logger";
const persistConfig = {
key: "root",
storage: ExpoFileSystemStorage,
whitelist: ["authReducer"],
};
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(
persistedReducer,
composeEnhancers(applyMiddleware(thunk, logger))
);
let persistor = persistStore(store);
export { store, persistor };
app.js
import React from "react";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { AppLoading } from "expo";
import { useFonts } from "#use-expo/font";
import { NavigationContainer } from "#react-navigation/native";
import { ThemeProvider } from "react-native-elements";
import { theme } from "./constants/ThemeConfiguration";
import { store, persistor } from "./store";
import RootNavigator from "./navigators/RootNavigator";
export default (App) => {
let [fontsLoaded] = useFonts({
"Lato-Regular": require("./assets/fonts/Lato-Regular.ttf"),
});
if (!fontsLoaded) {
return <AppLoading />;
} else {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<ThemeProvider theme={theme}>
<NavigationContainer>
<RootNavigator />
</NavigationContainer>
</ThemeProvider>
</PersistGate>
</Provider>
);
}
};
rootReducer.js
import thunk from "redux-thunk";
import AsyncStorage from "#react-native-community/async-storage";
import { createStore, applyMiddleware, compose } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import rootReducer from "./reducers/rootReducer";
import logger from "redux-logger";
const persistConfig = {
key: "auth",
storage: AsyncStorage,
};
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(
persistedReducer,
composeEnhancers(applyMiddleware(thunk, logger))
);
let persistor = persistStore(store);
export { store, persistor };
I got it fixed. Its not there in any documentation or article. The problem is with the persistConfig. Key which is root here. It should be the name of the reducer which we want to persist. In my case it was auth.
Updated persistConfig will be as follows:
const persistConfig = {
key: "auth",
storage: AsyncStorage,
};

Apollo with redux-persist

I integrated react-apollo for fetching data from my GraphQL server into my react native application. I use redux-perist for persisting and rehydrating the redux store.
But I don't know how to rehydrate my apollo state.
Anyone know how I can do that?
Thanks
You can use the autoRehydrate feature of redux-persist. It will asyncronously run an action with type REHYDRATE that will rehydrate all stores unless you blacklist them.
If you try to execute a query that was previously executed, Apollo will check your Redux store first and you should see an APOLLO_QUERY_RESULT_CLIENT action execute (meaning it's returning from the client store instead of querying the server). You can modify the fetchPolicy to specify how the query gets its data (network only, cache first, etc.)
Here's a basic setup:
import React, { Component } from 'react';
import { ApolloProvider } from 'react-apollo';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { persistStore, autoRehydrate } from 'redux-persist';
import { AsyncStorage } from 'react-native';
const networkInterface = createNetworkInterface({ uri: 'http://localhost:8080/graphql' });
export const client = new ApolloClient({
networkInterface: networkInterface,
});
const store = createStore(
combineReducers({
apollo: client.reducer(),
}),
{}, // initial state
compose(
applyMiddleware(client.middleware()),
autoRehydrate(),
),
);
// persistent storage
persistStore(store, {
storage: AsyncStorage, // or whatever storage you want
});
export default class App extends Component {
render() {
return (
<ApolloProvider store={store} client={client}>
<YourApp />
</ApolloProvider>
);
}
}

Cannot connect react native app w/ remote redux devtools

I cannot connect my react-native app using redux to redux-devtools-extension through remote-redux-devtools.
I am using android device connected by USB on macOSX Sierra.
// configureStore.js
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from 'remote-redux-devtools';
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const composeEnhancers = composeWithDevTools({ realtime: true });
export default function configureStore(initialState) {
const store = createStore(
rootReducer,
initialState,
composeEnhancers(
applyMiddleware(thunk),
)
);
if (module.hot) {
// Enable hot module replacement for reducers
module.hot.accept(() => {
const nextRootReducer = require('./reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
};
Redux is correctly configured into the app, w/ a simple button action to update redux state.
But it cannot found any store.
What am I missing ?
Try using this instead:
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

Persist store with Redux Toolkit in React-native

I am using redux-persist with redux toolkit.
here is my store configuration.
I haven't implemented or configured store before.
I intend to persist user state after login.
Currently after login, if I reload app in emulator it always goes back to login screen.
is my store configured properly?
updated store file:
import {configureStore} from '#reduxjs/toolkit';
import authReducer from '../features/login/authSlice';
import AsyncStorage from '#react-native-community/async-storage';
import {persistReducer, persistStore} from 'redux-persist';
import {combineReducers} from 'redux';
import hardSet from 'redux-persist/lib/stateReconciler/hardSet';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const reducers = combineReducers({
auth: authReducer,
// other reducers goes here...
});
const persistConfig = {
key: 'root',
storage: AsyncStorage,
// stateReconciler: hardSet,
};
const _persistedReducer = persistReducer(persistConfig, reducers);
export const store = configureStore({
reducer: _persistedReducer,
});
export const persistor = persistStore(store);
My index.js file where i wrap my root component with PersistGate
import React from 'react';
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import {store, persistor} from './src/stores/store';
import {Provider} from 'react-redux';
import {storeUser, storeRefreshToken} from './src/features/login/authSlice';
import {PersistGate} from 'redux-persist/lib/integration/react';
const RNRedux = () => (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
);
AppRegistry.registerComponent(appName, () => RNRedux);
Error I get currently:
I think you need to use persistStore. You should store your app store in persistStore and then use persistGate.
PersistGate delays the rendering of your app's UI until your persisted state has been retrieved and saved to redux.
So, your store file can be :
import { persistStore } from 'redux-persist';
const persistor = persistStore(store);
export { persistor, store };
And your app.js will be :
import { store, persistor } from 'path_to_your_store/store';
<Provider store={store}>
<PersistGate persistor={persistor}>
</App>
</PersistGate>
</Provider>
This appears to be an issue with redux-persist passing a function as an action type which is not recommended by the Redux devs. Redux Toolkit was developed with defaults to help avoid the creation of breakable code. It's RTK's default middleware "serializableCheck" that throws this error. This middleware can be disabled outright or just for specific actions, and both methods are provided by example in answers to this SO question. RTK middleware documentation here for quick reference.
Here's a blanket solution that should work for you:
- import {configureStore} from '#reduxjs/toolkit';
+ import {configureStore, getDefaultMiddleware} from '#reduxjs/toolkit';
...
export const store = configureStore({
reducer: _persistedReducer,
+ middleware: getDefaultMiddleware({
+ serializableCheck: false
+ }),
});
...