Why is Redux-Persist not persisting the store - reactjs

Hi there everyone I was messing around with react(redux to be precise) and came across a problem. When I refresh the page redux store does as well everything returns to its initial value, I googled the problem and found out that I have to use redux-persist. However even that is not working, I think the problem is with how I configured redux-persist but I could be wrong.
The code below is how I went about the redux-persist configuration.
// configureStore.js
import { createStore, applyMiddleware } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "../reducers/index";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
const persistConfig = {
key: "root",
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(
persistedReducer,
composeWithDevTools(applyMiddleware(thunk))
);
export const persistedStore = persistStore(store);
The code below shows how I went about making the rootReducer.
// index.js
import { combineReducers } from "redux";
import { reducer as formReducer } from "redux-form";
import authReducer from "./auth";
import messageReducer from "./message";
import chatroomReducer from "./chatroom";
import { LOGOUT_SUCCESS } from "../actions/authTypes";
const apiReducer = combineReducers({
authReducer,
messageReducer,
chatroomReducer,
form: formReducer,
});
const rootReducer = (state, action) => {
if (action.type === LOGOUT_SUCCESS) {
state = undefined;
}
return apiReducer(state, action);
};
export default rootReducer;
And the code below is the index.js that comes when creating the react app.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { PersistGate } from "redux-persist/integration/react";
import { Provider } from "react-redux";
import { store, persistedStore } from "./Redux/configureStore";
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistedStore}>
<React.StrictMode>
<App />
</React.StrictMode>
</PersistGate>
</Provider>,
document.getElementById("root")
);
reportWebVitals();
Can anyone tell me what is wrong with my code? If you need additional info please let me know.

Try configuring the whitelist, where you must indicate which reducers to store.
Remember to import the reducers before adding them to the list
import navigation from "./reducers/navigation";
// WHITELIST
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['navigation'] // only navigation will be persisted
};

First, you need to import storage from Async
import AsyncStorage from '#react-native-community/async-storage';
Use WhiteList like this
const persistConfig = {
key: 'root',
storage: AsyncStorage,
whitelist: [
'user'
],
};
"user" is the specific root reducer you want to store

As per this answer, you also need to clear out the redux-persist storage when you catch the LOGOUT_SUCCESS action.
First, you need to import the appropriate storage engine and then clear it out completely.
const rootReducer = (state, action) => {
if (action.type === SIGNOUT_REQUEST) {
// for all keys defined in your persistConfig(s)
storage.removeItem('persist:root')
// storage.removeItem('persist:otherKey')
return appReducer(undefined, action);
}
return appReducer(state, action);
};

Related

React-persist is not persisting state

I am trying to implement redux-persist in my reactjs application, i am trying to persist user info but whenever i do refresh all the information is cleaned up and i am redirected to the login page.
store.js
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
import rootReducer from "./state/reducer";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
const persistConfig = {
key: "primary",
storage,
whitelist: ["user", "artist"],
blacklist: ["alerts"],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const middleWare = [thunk];
const initialState = {};
export const store = createStore(
persistedReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleWare))
);
export const persistor = persistStore(store);
index.js for all reducers
import { combineReducers } from "redux";
import artistReducer from "./artistReducer";
import userReducer from "./userReducer";
import alertReducer from "./alertReducer";
export default combineReducers({
artist: artistReducer,
user: userReducer,
alerts: alertReducer,
});
App.js
import { persistor, store } from "./store";
import { PersistGate } from "redux-persist/integration/react";
function App() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<div style={{ backgroundColor: "grey" }}>
<Router>
<Fragment>
<Navbar></Navbar>
<Alerts></Alerts>
<Switch> ...{ code continues }
Here i can see my data but on refresh it gets cleaned up
After hours of exploring internet i found this issue locked on github [ https://github.com/rt2zz/redux-persist/issues/826#fromHistory ] and there was much voted comment on it by hackrit which states
"Hi guys,
I found a solution to this problem. (might not apply to all the cases, but worked for me).
The problem was caused by my default return in my reducer, which I returned {...state}.
Once I changed the default return to just return state, everything work as expected." and this suggestion worked for me.
In My case this same issue happen after updating expo version.
Solution:
// import FSStorage from "redux-persist-expo-fs-storage";
import FSStorage from "redux-persist-fs-storage";
/*
Import change
*/
const primary = {
key: "root",
timeout: 0,
version: 1,
keyPrefix: "",
storage: FSStorage(),
stateReconciler: autoMergeLevel2, // see "Merge Process" section for details.
};
ADD keyPrefix: "", in config.
Hope this solutions work for others.

How to persist redux store to local storage rather than async storage in react native

I am using redux-persist to persist state of my app. By default it persist state inside AsyncStorage but I want it to be in localStorage. There is nothing about this in official docs as far as react-native is concerned.
This is my code till now:
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/es/storage';
const config = {
key: 'root',
storage,
debug: true
}
export const ConfigureStore = () => {
const store = createStore(
persistCombineReducers(config,{
dishes,
comments,
promotions,
leaders,
favorite
}),
applyMiddleware(thunk)
);
const persistor = persistStore(store)
return {persistor, store};
}
Can I do something like this:
import storage from 'redux-persist/es/localStorage';
...
storage: storage
Please help me on the topic if anyone has ever used it before. Thank You!
One thing i noticed is that you import says
import storage from 'redux-persist/es/storage';
But in the documentation is used
import storage from 'redux-persist/lib/storage'
Otherwise you can use this approach like in the documentation.
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
import rootReducer from './reducers'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store)
return { store, persistor }
}
import { PersistGate } from 'redux-persist/integration/react'
// ... normal setup, create store and persistor, import components etc.
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<RootComponent />
</PersistGate>
</Provider>
);
};

React- How to make states persists after page refresh?

I made a single page application using React.js with a login page and perfil page. All works well but recently I noticed when refresh my page, all states are empty. Please someone can say me how to fix that issue, I mean what library import and where add it
my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App.jsx';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
this is my App.jsx
import React from 'react';
import '../App.css';
import AppRoutes from './AppRoutes';
import { Provider } from "react-redux";
import store from '../redux/store'
store.dispatch(getTallerList())
const App = () => (
<Provider store={store}>
<AppRoutes />
</Provider> )
export default App;
and my store.js
import { applyMiddleware, combineReducers, createStore } from 'redux'
import { ADD_TO_CART, GET_COURSE_LIST, USUARIO_LOGIN } from './action'
import { composeWithDevTools } from 'redux-devtools-extension'
import { persistStore, persistReducer } from 'redux-persist'
import thunk from 'redux-thunk'
const initialCart = {
cart:[]
}
const initialCourses ={
courses:[]
}
const initialUser ={
user:{}
}
const cartReducer = ( state = initialCart,action) => {
if(action.type===ADD_TO_CART)
{
if(state.cart.find(c=>c===action.id))
{
return state
}
return{
...state,
cart: state.cart.concat(action.id),
}
}
return state
}
const coursesReducer = (state=initialCourses, action) =>{
console.log(action)
if(action.type === GET_COURSE_LIST){
return {
...state,
courses: action.courses
}
}
return state
}
const userReducer = (state=initialUser, action)=>{
console.log(action)
if(action.type === USER_LOGIN){
return {
...state,
user: action.user
}
}
return state
}
export default createStore(combineReducers({cartReducer, coursesReducer, userReducer}), composeWithDevTools(applyMiddleware(thunk)))
Try to store value in local storage and at the time of page load get value from local storage. If you have some more values you should use redux for data storage.
It's not an issue, it's the way it works. When you refresh the entire app builds once again from the scratch.
But to persist the store upon refresh you can use these redux middlewares -
redux-persist or
redux-storage
Use this configuration as you are using redux-persist. This is my configuration, just change the main app, reducers, and actions according to your need.
import React from 'react';
import ReactDOM from 'react-dom';
import MyApp from './MyApp';
import {Provider} from 'react-redux';
import {applyMiddleware, createStore} from "redux";
import allReducers from './reducers/index';
import {persistReducer, persistStore} from 'redux-persist';
import {PersistGate} from 'redux-persist/integration/react';
import storage from 'redux-persist/lib/storage';
import thunk from "redux-thunk";
const persistConfig = {
key: 'root',
storage,
};
const persistedReducer = persistReducer(persistConfig, allReducers);
let store = createStore(persistedReducer, applyMiddleware(thunk));
let persistor = persistStore(store);
ReactDOM.render(<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<MyApp/>
</PersistGate>
</Provider>, document.getElementById('root'));
Updated : you can do like this
import {combineReducers} from "redux";
import Users from './load-user';
const allReducers = combineReducers({
users: Users,
})
export default allReducers

Redux-persist is not working: Page refresh clears the state

To persist the state when the page is refreshed, I am trying to integrate redux-persist. However, it is not working. Page refresh clears the state. Here is how _persist object int the state looks:
_persist: {
version: -1,
rehydrated: true
}
Here is the configureStore.js:
import { createStore, applyMiddleware, compose } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import rootReducer from "./_reducers/rootReducer";
import storage from "redux-persist/lib/storage";
import { persistStore, persistReducer } from "redux-persist";
const persistConfig = {
key: "root",
storage,
whitelist: []
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const middlewares = [thunk];
// if (__DEV__) react native check dev
middlewares.push(logger);
const store = createStore(
persistedReducer,
{},
compose(
applyMiddleware(...middlewares),
window.devToolsExtension ? window.devToolsExtension() : f => f)
);
const persistor = persistStore(store);
export { store, persistor };
And, here is the index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from 'react-router-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { PersistGate } from "redux-persist/lib/integration/react";
import { store, persistor } from "./configureStore";
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const rootElement = document.getElementById('root');
ReactDOM.render(
<Router basename={baseUrl} history={history}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</Router>,
rootElement);
registerServiceWorker();
I cannot figure out the problem with the code. Any help?
const persistConfig = {
key: "root",
storage,
whitelist: ["pass reducer name which you want to persist in string"] e.g: whitelist: ["userAuth", "widgetAuth"]
};
if you want your whole state persist than remove whitelist key from persistConfig
For React Native Expo.
In My case this same issue happen after updating expo version.
Solution:
// import FSStorage from "redux-persist-expo-fs-storage";
import FSStorage from "redux-persist-fs-storage";
/*
Import change
*/
const primary = {
key: "root",
timeout: 0,
version: 1,
keyPrefix: "",
storage: FSStorage(),
stateReconciler: autoMergeLevel2, // see "Merge Process" section for details.
};
ADD keyPrefix: "", in config.
Hope this solutions work for others.

How to get store from redux-persist on app load in react-native?

I have a problem running react-native app. I've got a store that uses redux-persist and some other middleware and the problem is when the app loads, data from the store is not there, only the default values. However as soon as I touch any button the data is there, so the problem is I can't delay rendering until the store is rehydrated. I've tried options with wrapping the app with PersistGate with both loading and bootstrapped values, however this does not work for me. The current code is:
Store config:
import { createStore, applyMiddleware, compose } from 'redux';
import promise from 'redux-promise-middleware';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import { AsyncStorage } from 'react-native';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import rootReducer from './reducers';
const middleware = [
promise(),
thunk,
];
if (__DEV__) { // eslint-disable-line
// middlewares.push();
}
const enhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // eslint-disable-line
const persistConfig = {
key: 'root',
storage: AsyncStorage,
stateReconciler: autoMergeLevel2,
};
const pReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(pReducer, {}, enhancers(applyMiddleware(...middleware)));
export const persistor = persistStore(store);
Root Reducer:
import { combineReducers } from 'redux';
import loginScreenReducer from 'screens/login/LoginScreen.reducer';
export default combineReducers({
loginScreenReducer,
});
App:
import { AppLoading } from 'expo';
import React from 'react';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import Root from 'Root.component';
import { store, persistor } from 'reduxConfig/store';
export default class App extends React.Component {
state = { }
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
{(bootstrapped) => {
if (bootstrapped) {
console.log(store.getState());
return <Root />;
}
return <AppLoading />;
}}
</PersistGate>
</Provider>
);
}
}
The component I'd like to make work correctly:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Navigator from './Navigator';
import { LoginScreen } from '../screens';
#connect(
state => ({
token: state.loginScreenReducer.token,
isLogged: state.loginScreenReducer.isLogged,
}),
)
export default class AppNavigator extends Component {
state = { }
render() {
const {
token,
} = this.props;
if (token) {
return <Navigator />;
}
return <LoginScreen />;
}
}
export const { router } = Navigator;
The component shows login screen first but if I touch any button on it, it renders Navigator component. Also when I get the state of the store (App, before returning <Root />, it has the next structure, containing empty object, which is currently default for loginScreenReducer:
The app works correctly except for redux-persist.
===== New Answer =====
The old answer is wrong, the problem came back later. It looks like it's a problem with redux-persist, or even expo, react-native-scripts or whatever, I was not been able to locate the problem source. The behavior I described before comes from redux-persist inability to access AsyncStorage. However the only thing that helps now is completely stopping expo and running it again, after that redux-persist is able to access AsyncStorage again.
It also looks like disabling live reload really helps with this problem if not stops it at all.
===== Old Answer =====
I've ended up by downgrading react-redux package to version 4.4.9, looks like this has solved the problem, otherwise the code I posted initially should be totally okay. The problem with auto-filling state on button press was caused my a couple of my own mistakes both from BE and FE sides, that lead to store be filled with different items. They are not in the given code. Also I've changed my store a little, I'm not sure if the initial problem was connected to enhancers combining. So now my store looks like this:
import { createStore, applyMiddleware } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import { AsyncStorage } from 'react-native';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise-middleware';
import { createLogger } from 'redux-logger';
import { LOGOUT } from 'screens/login/LoginScreen.actions';
import appReducer from './reducers';
const middlewares = [
promiseMiddleware(),
thunk,
];
if (__DEV__) { // eslint-disable-line
middlewares.push(createLogger());
}
const rootReducer = (state, action) => {
if (action.type === LOGOUT) {
state = undefined;
}
return appReducer(state, action);
};
const persistConfig = {
key: 'root',
storage: AsyncStorage,
stateReconciler: autoMergeLevel2,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(persistedReducer, undefined, applyMiddleware(...middlewares));
export const persistor = persistStore(store);
I hope this will help somebody.

Resources