I'm trying to persist specific reducer state to localStorage so that it wouldn't be disappear even though the browser is refreshed.
I've written some code, but in the storage all I can see is just like this:
"persist:root", "{"_persist":{\"version\":-1,\"rehydrated\":true}"}"
Tthere's no user
data. What am I missing?
FYI, the reason why I need to store user data as global variable is that I have to re-render depending on this value. The problem is that when the browser is refreshed, it keeps navigating to loginPage. I've checked redux dev tools, the user data is set properly in the Login
component.
store.js
import { configureStore, createSlice } from '@reduxjs/toolkit'
import storage from 'redux-persist/lib/storage';
import { persistReducer } from 'redux-persist';
import { persistStore } from 'redux-persist';
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['user']
};
let user = createSlice({
name: 'user',
initialState: {
userId: 'anonymous',
isLoggedIn: false
},
reducers: {
changeUser(state, action) {
state.userId = action.payload.userId;
state.isLoggedIn = action.payload.isLoggedIn;
}
}
})
const persistedReducer = persistReducer(persistConfig, user.reducer);
export let { changeUser } = user.actions;
let store = configureStore({
reducer: {
user: persistedReducer
}
// persistedReducer
})
export const persistor = persistStore(store);
export default store;
App.js
function App(){
const isLoggedIn = useSelector((state) => state.user.isLoggedIn);
return (
<BrowserRouter>
<RedirectToLogin />
<Center>
{isLoggedIn && <Sidebar userRole={''} />}
<Routes>
<Route path="/loginPage" element={<Login />} />
{isLoggedIn ? (
<>
<Route element={<ProtectedRoute isLoggedIn={isLoggedIn} />} />
<Route path="/" element={<Editor/>}/>
<Route path={"/loginPage"} element={<Login/>}/>
<Route path="*" element={<NotFoundPage/>}/>
</>
) : (
<>
<Route path="*" element={<Navigate to="/loginPage" replace />} />
</>
)}
</Routes>
</Center>
</BrowserRouter>
)
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store, {persistor} from "./store";
import {Provider} from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</React.StrictMode>
);
reportWebVitals();
The issue it seems is with the persistence configuration, specifically what it whitelists, and what reducer function it is persiting:
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['user']
};
This persists the user
key of the reducer you are persisting, e.g. user.reducer
, but the user
slice's reducer doesn't have any user
key to persist, it's just the direct user.reducer
function.
const user = createSlice({
name: 'user',
initialState: {
userId: 'anonymous',
isLoggedIn: false
},
reducers: {
changeUser(state, action) {
state.userId = action.payload.userId;
state.isLoggedIn = action.payload.isLoggedIn;
}
}
})
const persistedReducer = persistReducer(persistConfig, user.reducer);
Either persist the entire user.reducer
function, e.g. no whitelisting in the user.reducer
:
import { configureStore, createSlice } from '@reduxjs/toolkit';
import storage from 'redux-persist/lib/storage';
import { persistReducer } from 'redux-persist';
import { persistStore } from 'redux-persist';
const user = createSlice({
name: 'user',
initialState: {
userId: 'anonymous',
isLoggedIn: false
},
reducers: {
changeUser(state, action) {
state.userId = action.payload.userId;
state.isLoggedIn = action.payload.isLoggedIn;
}
}
});
export const { changeUser } = user.actions;
const persistConfig = {
key: 'root',
storage: storage,
};
const persistedUserReducer = persistReducer(persistConfig, user.reducer);
const store = configureStore({
reducer: {
user: persistedUserReducer
}
})
export const persistor = persistStore(store);
export default store;
Or create a root reducer function, e.g. "combineReducers", and whitelist the user
state.
import {
configureStore,
createSlice,
combineReducers
} from '@reduxjs/toolkit';
import storage from 'redux-persist/lib/storage';
import { persistReducer } from 'redux-persist';
import { persistStore } from 'redux-persist';
const user = createSlice({
name: 'user',
initialState: {
userId: 'anonymous',
isLoggedIn: false
},
reducers: {
changeUser(state, action) {
state.userId = action.payload.userId;
state.isLoggedIn = action.payload.isLoggedIn;
}
}
});
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['user']
};
export const { changeUser } = user.actions;
const rootReducer = combineReducers({
user: user.reducer,
});
const persistedRootReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({
reducer: persistedRootReducer,
})
export const persistor = persistStore(store);
export default store;