Open kg912 opened 6 years ago
@kg912 you shouldn't need to manually call persistor.flush(). On redux state change, if the reducer key is whitelisted, it should automagically store in localStorage the reducer state. Did you check using redux dev tools that the ui user state actually change after dispatching an action in the reducer ?
Hi tommyalvarez, I have verified that the component level state does change as I update my application level (Redux) state. My problem is that the initial state is being persisted on refresh instead of the newer application state. My root reducer takes in the following differrent reducers
import Auth from './auth/reducer';
import App from './app/reducer';
import Mails from './mail/reducer';
import Course from './course/reducer';
export default {
Auth,
App,
Mails,
Course
};
and each reducer stores its state in an immutable map like this:
import courseActions from './actions';
import { Map } from 'immutable';
const course_data = {};
const { COURSE_DATA, COURSE_UPDATE, COURSE_MAT_UPDATE, REMOVED_MATERIAL, RETRIEVE_COURSE, COURSE_ASSIGN_UPDATE} = courseActions;
const initState = new Map({
course_data,
});
export default function(state = initState, action) {
switch(action.type) {
case COURSE_UPDATE:
const { courseId, newCourse } = action.payload;
let data = state.get('course_data');
data[courseId] = newCourse;
state.set('course_data', data);
return state.set('course_data', data);
break;
case COURSE_DATA:
const { course, id } = action;
let old_state = state.get('course_data');
return state.set('course_data', { ...old_state, [id]: course});
break;
case COURSE_MAT_UPDATE:
const { materials, cId} = action;
let courseData = state.get('course_data');
courseData[cId].materials = materials;
return state.set('course_data', courseData);
}
}
Do I need to explicitly whitelist all of these reducers? also do I need to pass anything at all to the immutableTransform function?
@kg912 No, if you want to persist all your reducers states you should not specify either whitelist nor blacklist arguments. However, i would recommend whitelisting so only the parts of the state that you really need persisted, are actually persisted. Otherwise you could end up with a heavy persisted state into localStorage. That aside, after state change, go check in your chrome console Application tab, if in the localStorage your data is correctly persisted.
NOTICE: I'm seeing in your code that you are using combineReducers. In redux-persist v5, you need to use persistCombineReducers, though it's a little bit hidden, i think it's not in the docs but i read it up on a blog on medium or don't remember quite well but if you google persisteCombineReducers you will see... So, it should be something like this:
import { persistCombineReducers } from 'redux-persist'
const persistedReducer = persistCombineReducers(persistConfig, {
your_reducers,
...asyncReducers
}));
If not working, the probable causes are:
<PersistGate>
component ? That component with the argument persistor is what prevents rendering the app until the store is rehydrated with data coming from the localStorage.But in conclusion you should neither call persistor.flush() nor manually dispatch any redux-persist reducers actions like persist/REHYDRATE, persist/PERSIST, etc... it works out of the box when correctly setup.
And to your last question, i don't have experience with redux-persist and immutable transforms because i don't use the immutable lib, so i don't know what to answer to that. If you're still banging your head against the wall, try persisting just one of your reducers state without using immutable, make it work and start from there.
I know this is a bit late, but for people in the future having similar problems..
I am assuming what's happening is (this was the case for me at least) is upon refresh, redux-logger
shows the correct payload to set during rehydrate
, but the next state in logger
is showing the reducer's initial state. I noticed this was only happening when I had nested objects (for example, when I was trying to persist strings, it worked fine.
The solution (which is now in the docs, maybe not at the time of the original post) is to use nested persists. I noticed in OP's code:
const persistedReducer = persistReducer(persistConfig, combineReducers({
...reducers,
router: routerReducer
}));
which is similar to what I was doing. I had to explicitly use persistReducer
on each reducer in my combineReducers
(or at least the ones I was going to persist, such as user object).
So I would propose either doing this programmatically or just manually doing it for each reducer in before combining them. For me, this was:
from
const rootReducer = combineReducers({
form: formReducer,
user: userReducer,
});
to
const rootReducer = combineReducers({
form: formReducer,
user: persistReducer(persistConfig, userReducer),
});
Similar to OP's code, I had:
const persistedReducer = persistReducer(persistConfig, reducer);
// this won't work with nested objects in reducers (or at least I don't think)!
where reducer
was the rootReducer from my reducer file.
I had to change some imports
around but now my user object persists properly! And I didn't have to use hardSet
(which was persisting properly, but wouldn't have allowed me to add more "whitelist"/reducers in the future without checking storage somehow or force rewriting storage)!
Here are some snippets of my working code for reference (with some helpful comments). Also my repo:
reduxPersist.js
import storage from 'redux-persist/lib/storage';
const KEY = 'storage-key-here';
export const persistConfig = {
key: KEY,
storage,
};
reducers.js
import { combineReducers } from 'redux';
import { persistReducer } from 'redux-persist';
import { persistConfig } from '../pkgs/reduxPersist';
// had to move this here from reduxPersist.js, which is where it was originally
import formReducer from './form';
import userReducer from './user';
const rootReducer = combineReducers({
form: formReducer,
user: persistReducer(persistConfig, userReducer), // put persistReducer here
// you can also add custom configs for each reducer (e.g. in the nested persists doc)
});
export default rootReducer;
_store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reducer from './reducers'; // import rootReducer here
const devMiddleware = [];
const middleware = [];
if (process.env.NODE_ENV === 'development') {
devMiddleware.push(logger);
// add more dev middleware here
}
middleware.push(thunk);
// add more middleware here
const store = createStore(
reducer, // don't put persistReducer here
applyMiddleware(...middleware.concat(devMiddleware)),
);
export default store;
App.js
// other import statements
import { PersistGate } from 'redux-persist/integration/react';
import { persistStore } from 'redux-persist';
// some code
import store from '../_store';
// more code
class App extends Component {
render() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistStore(store)}>
{/* more code
*/}
</PersistGate>
</Provider>
);
}
}
export default App;
hope this helped!
Thank @samscha, It's worked. You save my day
In this case, proposed by @samscha i had only 1 reducer, that work fine. Other not persist state.
Hi,
I am using redux-persist with my entire state tree being stored as an immutable map as shown in my store.js file code below.
My Problem is that even after I make changes to the state tree (which I have confirmed, do in fact take place as they should, using redux logger), the changes aren't shown in my application on refresh despite the fact that I call persistor.flush() like this:
right after the state change (Also verified that that the flush action fires). I've tried everything and I would really appreciate any advice whatsoever, on the matter. Thanks in advance.