Asked 1 month ago by CosmicResearcher647
Why Is My Redux State Not Persisting on Page Refresh in ReactJS?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by CosmicResearcher647
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I'm using redux-persist to store authentication data in my Redux state. The data is correctly saved when navigating to another page after signing in, but when I refresh the page, the persisted state is lost.
I have tried various approaches including using and omitting the whitelisting parameter. Below are my relevant code snippets:
persistStorage.js
JAVASCRIPTimport { configureStore } from '@reduxjs/toolkit'; import { persistStore, persistCombineReducers } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import authReducer from './authStorage' const persistConfig = { key: 'root', storage, serializableCheck: { ignoredActions: ['SOME_ACTION'], warnOnly: true, } }; const persistedReducer = persistCombineReducers(persistConfig, authReducer); const store = configureStore({ reducer: persistedReducer }); export const persistor = persistStore(store);
authStorage.js
JAVASCRIPTimport { createSlice } from '@reduxjs/toolkit'; const initialState = { token: "", id: "" }; const authSlice = createSlice({ name: 'auth', initialState, reducers: { setToken(state, action) { state.token = action.payload; }, setID(state, action) { state.id = action.payload; }, clear(state) { state.token = null; state.id = null; }, }, });
SignIn.js
JAVASCRIPTexport const { setToken, setID, clear } = authSlice.actions; export default authSlice.reducer; import { useState, useRef } from "react"; import { useMutation } from "@apollo/client"; import { useNavigate } from "react-router-dom"; import { useCookies } from "react-cookie"; import Header from '../components/Header'; import InputField from "../components/InputField"; import { useDispatch, useSelector } from 'react-redux'; import { auth } from "../../index"; import { setToken, setID } from '../../authStorage'; import { SIGN_IN } from "../../queries/accountQueries.js"; import "../../style/signIn.css"; function SignIn() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [ signIn, { data, loading, error }] = useMutation(SIGN_IN); const [ cookies, setCookie ] = useCookies(['session']) const navigate = useNavigate(); const dispatch = useDispatch(); let signedIn = useRef(false); const setValue = (newValue, valueType) => { if (valueType === "Email") setEmail(newValue); else setPassword(newValue); } if (loading) return <p>Loading...</p>; else if (error) { alert("error"); return <p>Error :({JSON.stringify(error, null, 2)}</p>; } else if (data != undefined && !signedIn.current) { signedIn.current = true dispatch(setToken(data.employeeSignIn.auth)); // stores data with Redux dispatch(setID(data.employeeSignIn.id)); auth.signInKey = null; setCookie("session", data.employeeSignIn.session, { options: { maxAge: 7200 } }) navigate('/two-factor-auth'); } return( <> <Header view={false} /> <div className="content centered flex col"> <form className="centered" onSubmit={(e) => { e.preventDefault(); auth.signInKey = process.env.REACT_APP_SIGN_IN_KEY; signIn({ variables: { email: email, password: password } }) }}> <h2>Sign in</h2> <InputField label="Email" value={email} onChangeState={setValue} /> <InputField type="password" label="Password" value={password} onChangeState={setValue} /> <input type="submit" className="button centered"/> </form> </div> </> ) } export default SignIn;
index.js
JAVASCRIPTroot.render( <ApolloProvider client={client}> <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <RouterProvider router={router} /> </PersistGate> </Provider> </ApolloProvider> );
I have reviewed several posts related to this issue (see linked posts below), but haven't found the solution yet.
Persist Store with Redux in React Native
Passing reducer name to Redux-persist whitelist is not working
redux-persist is not saving my redux state when I refresh or close my react-native app
Comments 0
•Answers 3
•Views 32
Answer by TychoAI Agent • 1 month ago
Based on your code and the common pitfalls with redux‐persist, one likely issue is how you’re combining reducers. In your persistStorage.js you have:
JAVASCRIPTconst persistedReducer = persistCombineReducers(persistConfig, authReducer);
The function persistCombineReducers expects an object mapping of slice names to reducers. When you only have a single reducer, you have two options:
Wrap your reducer in an object:
JAVASCRIPTconst persistedReducer = persistCombineReducers(persistConfig, { auth: authReducer });
Then update your store accordingly. Keep in mind that you’ll need to adjust your selectors since your state will be nested (e.g., state.auth.token).
Alternatively, use persistReducer if you have only one reducer:
JAVASCRIPTimport { persistReducer } from 'redux-persist'; // ... const persistedReducer = persistReducer(persistConfig, authReducer);
Then configure your store with this persisted reducer.
Both approaches ensure redux‑persist is correctly combining or wrapping the reducer, which is likely why your updated state is lost upon refresh. Make sure after making these changes that your PersistGate is correctly set up in index.js, as you already have:
JAVASCRIPTroot.render( <ApolloProvider client={client}> <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <RouterProvider router={router} /> </PersistGate> </Provider> </ApolloProvider> );
Once you correct how your reducer is set up for persistence, your Redux state should rehydrate correctly after a page refresh.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by StellarScientist935 • 1 month ago
The issue is that you are using persistCombineReducers
which expects to consume an object of ReducersMapObject
, specifically
TYPESCRIPTReducersMapObject<CombinedState<S>, Action<any>>
TYPESCRIPT// combineReducers + persistReducer with stateReconciler defaulted to autoMergeLevel2 // eslint-disable-next-line @typescript-eslint/no-unused-vars export default function persistCombineReducers<S, A extends Action>( config: PersistConfig<any>, reducers: ReducersMapObject<CombinedState<S>, Action<any>> ): Reducer<any, AnyAction> { config.stateReconciler = config.stateReconciler === undefined ? autoMergeLevel2 : config.stateReconciler return persistReducer(config, combineReducers(reducers)) }
but you are passing it a reducer function, e.g.
TYPESCRIPTReducer< { token: string; id: string; }, UnknownAction, { token: string; id: string; } >
The result is that your store is malformed and none of your state is persisted to storage:
Store | redux-devtools |
Storage | local-storage |
Either pass an object of reducers to persistCombineReducers
:
TYPESCRIPTconst persistedReducer = persistCombineReducers(persistConfig, { auth: authReducer, });
Store | redux-devtoolse |
Storage | local-storage |
or use persistReducers
to handle the single auth reducer function:
TYPESCRIPTimport { persistStore, persistReducer } from "redux-persist"; ... const persistedReducer = persistReducer(persistConfig, authReducer);
Store | redux-devtools |
Storage | local-storage |
No comments yet.
Answer by AstroGuide965 • 1 month ago
The issue was that I imported store
from a separate file than from persistStorage
in index.js
.
I had this:
JAVASCRIPTimport store from './store' import { persistor } from "./persistStorage"
I should have had this:
JAVASCRIPTimport { persistor, store } from "./persistStorage"
No comments yet.
No comments yet.