redux-persist not updating local storage even after successful dispatch - reactjs

In my app on log out I'm doing this.props.dispatch(setUserSession({})) even passed null here. After dispatching in redux-logger I can see that it's changed to
action {type: "SET_USER_SESSION", userSession: {}}
next state {userSession: {}
But in local storage, I can still see the userSession that was there before dispatching null.
On what action or when will the persisted store will get updated.
I'm setting userSession to null on logout, but when the user refreshes the page, he is back in without login since the token is present in the store.
And also I don't want to do a purge or flush the full store, just that userSession key.
Current store configuration
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import reducer from './reducers';
let middleware = [thunk];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const configureStore = composeEnhancers(applyMiddleware(...middleware))(createStore);
const config = {
key: 'root',
storage
};
const combinedReducer = persistReducer(config, reducer);
const store = configureStore(combinedReducer);
const persistor = persistStore(store);
export { persistor, store };
Kindly provide some help, correct me if I'm missing something.

Related

redux-persist whitelist not working with rtk

I am using the createSlice function from the rtk library to create multiple reducers in my Redux store. I am also trying to use redux-persist to save certain slices to local storage.
this store.js code is working well:
import { combineReducers, configureStore } from "#reduxjs/toolkit";
import betSlice from "./features/betSlice";
import configSlice from "./features/configSlice";
import eventSlice from "./features/eventSlice";
import userDataSlice from "./features/userDataSlice";
import wsSlice from "./features/wsSlice";
import storage from "redux-persist/lib/storage";
import { persistReducer, persistStore } from "redux-persist";
import thunk from "redux-thunk";
import modalsSlice from "./features/modalsSlice";
const persistConfig = {
key: "root",
storage,
};
const presistedReducers = combineReducers({
wsSlice: persistReducer(persistConfig, wsSlice),
eventSlice: persistReducer(persistConfig, eventSlice),
betSlice: persistReducer(persistConfig, betSlice),
configSlice: persistReducer(persistConfig, configSlice),
userDataSlice: persistReducer(persistConfig, userDataSlice),
modalsSlice: persistReducer(persistConfig, modalsSlice),
});
export const store = configureStore({
reducer: presistedReducers,
middleware: [thunk],
});
export const persistor = persistStore(store);
I am having trouble using the whitelist\blacklist options provided by redux-persist. when I try to use a whitelist to only persist the configSlice reducer like this:
const persistConfig = {
key: "root",
storage,
whitelist: ["configSlice"],
};
nothing is saved to local storage.
Similarly, when I try to use a blacklist, all of the reducers are saved to local storage.
All of the slices in my project are structured in the same way, using the createSlice function in this format:
import { createSlice } from '#reduxjs/toolkit';
const initialState = {};
const nameSlice = createSlice({
name: 'nameSlice',
initialState,
reducers: {},
});
export const {} = nameSlice.actions;
export default nameSlice.reducer;
Can anyone explain why the whitelist and blacklist options are not working as expected, and how I can correctly configure redux-persist to only save certain slices to local storage?

redux-resist losing data on refresh

My App Component
My Store
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);
let store = createStore(persistedReducer);
let persistor = persistStore(store);
export { store, persistor };
localStorage
After refresh / reload the cartItems become empty
package info
react: "^17.0.2",
react-redux: "^7.2.3",
redux-persist: "^6.0.0",
I can see data being store on localStorage but on page refresh, i am losing the data
Add whitelist to your persistConfig variable by passing "auth" only auth reducers will be persisted. you can define as many as reducers in the whitelist you want to be persisted. This should resolve your issue.
const persistConfig = {
key: 'root',
storage,
whitelist: ["auth"] //
};
Go through this npm Readme for more clarification: https://www.npmjs.com/package/redux-persist#blacklist--whitelist

redux-presist not updating local storage a browser

I'm using redux-persist for my e-commerce project.
When I updated my app to new version I found a new bug.
I changed my initialState from array into object.
My app in Chrome (browser that i test and run my app on it) crashed !! because my initialState still array(NOT UPDATE) and new method was not returned anything. (Of course).
But in Mozilla and other Browsers that I not use before them my app working currently !
why!?
My Store Config :
import { createStore, applyMiddleware } from 'redux'
import { persistStore } from 'redux-persist'
import logger from 'redux-logger'
import RootReducer from './RootReducer'
const middleware = [logger]
export const store = createStore(RootReducer, applyMiddleware(...middleware))
export const persistore = persistStore(store)
My rootReducer Config :
import { combineReducers } from 'redux'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import UserReducer from './User/UserReducer'
import ShoppingBagDropdownReducer from './ShoppingBagDropdown/ShoppingBagDropdownReducer'
import DirectoryReducer from './Directory/DirectoryReducer'
import ShopDataReducer from './ShopData/ShopDataReducer'
const persistConfig = {
key: 'root',
storage,
whileList: ['shoppingBagDropdown']
}
const rootReducer = combineReducers({
user: UserReducer,
shoppingBagDropdown: ShoppingBagDropdownReducer,
shopData: ShopDataReducer,
directory: DirectoryReducer
})
export default persistReducer(persistConfig, rootReducer)
Yeah :)
I found solution and that is : clear local storage with :
localStorage.clear();
I saw that you fixed it, but you could also do it without code using google chrome.
CTRL + SHIFT + I > Application > Local Storage
Now you just have to open the localstorage, right click and click on clear.

How to persist to both localStorage and sessionStorage at the same time using redux-persist?

I'm implementing a login form using react, redux and redux-persist. Login API is ready from back-end team. The form has a "remember me" checkbox and I want to persist JWT auth token from login response.
What I think is the right way to implement "remember me" is this:
If "remember me" is not checked, token should be persisted to
sessionStorage. So that when browser is closed, user should login again but does not need login on switching tab or refreshing current page.
If it is checked, token should be persisted to localStorage. User is still logged in even if the browser is closed.
Now I know from our back-end perspective there is only token expiration time.
My first question is if my client side view and approach is correct or not.
If it is, how can I update my persist config based on login "remember me" checkbox?
My current store.js is this:
import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import rootReducer from './reducers';
const persistConfig = {
key: 'root',
storage,
whitelist: ['auth'],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer);
const persistor = persistStore(store);
export {
store, persistor
};
And it is used here:
import React from 'react';
import ReactDOM from 'react-dom';
import App from 'js/containers/App';
import { store, persistor } from './store';
const render = Component => {
ReactDOM.render(
<Component store={store} persistor={persistor}/>,
document.getElementById('root')
)
};
render(App);
if (module.hot) {
module.hot.accept('js/containers/App', () => {
const NextApp = require('js/containers/App').default;
render(NextApp);
});
}
I want to change persistConfig based on login form and use sessionStorage instead of localStorage but it's like store structure is formed here and there is no way to change it in app.
Any idea how to implement this?
I don't think you can change storage after creating but you can have 2 sub-tree on different storages. So tick/untick "remember me" will save in different trees and make a function to get the token from both sources.
to create sub-tree on difference storage see this
https://github.com/rt2zz/redux-persist#nested-persists
Edit
Adding sample for clarity.
import { combineReducers } from 'redux'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import storageSession from 'redux-persist/lib/storage/session'
const rootPersistConfig = { key: 'root', storage, whitelist: ['cart','user'] };
const authPersistConfig = { key: 'user', storage:storageSession }; // This is to session storage
const rootReducer = combineReducers({
user: persistReducer(authPersistConfig,userReducer), // user subtree saved in session storage
cart: cartReducer,
directory: directoryReducer,
shop: shopReducer });
export default persistReducer(rootPersistConfig, rootReducer);

connect multiple combined reducers in redux with conditional

I have an app that have different access level, like admin, guest and normal user, so it has several roles. My folder structure is very clear, some components are shared like Button, Loader but reducers and actions are not shared, because they are completely different of apps.
I did this to do conditioning setting for my store (Yes I only need one store because the entry for all type of the user is the same)
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { getUserRole } from './utils' // merely localstorage util to get roles
import { composeWithDevTools } from 'redux-devtools-extension'
import userReducers from './reducers'
import adminReducers from './reducers/admin'
//share reducer btw member and admin
let reducers
if (getUserRole() === 'member') {
reducers = userReducers
} else {
reducers = adminReducers
}
console.log('reducers', reducers) //undefined during the first load, most likely store is done setup before localstorage?
const store = createStore(
reducers,
composeWithDevTools(
applyMiddleware(thunk)
)
)
export default store
The problem is reducers is undefined unless I refresh the entire page.
Maybe the problem is that localStore is not async according to this SO answer.
So by returning a Promise you'll make sure getUserRole() is not undefined:
export function getUserRole(){
return new Promise((resolve, reject) => {
let role = localStorage.getItem('token').decode('role')
resolve(role)
})
}
and in index.js:
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { getUserRole } from './utils' // merely localstorage util to get roles
import { composeWithDevTools } from 'redux-devtools-extension'
import userReducers from './reducers'
import adminReducers from './reducers/admin'
//share reducer btw member and admin
let store
getUserRole().then(role => {
reducers = role === 'member'
? userReducers
: adminReducers
console.log('reducers', reducers)
store = createStore(
reducers,
composeWithDevTools(
applyMiddleware(thunk)
)
)
})
export default store
Tell me if something went wrong.

Resources