I have a substate called 'app' and my reducer does not listen on the REHYDRATE action type but it has an initial state.
Despite this, I get the following logs: redux-persist/autoRehydrate sub state for key "app" modified, skipping autoRehydrate.
What does it mean in detail and how can it be resolved?
Relevant code: https://github.com/rt2zz/redux-persist/blob/master/src/autoRehydrate.js#L60
It seems that if the reducer returns the initial state then it does not get rehydrated.
When I pass the initial state to the redux createStore function instead of the reducer, rehydration works.
Is this the expected behaviour?
Hm, I will have to look into this. The check in autoRehydrate is if state[key] !== reducedState[key]
so as long as your reducer does not handle REHYDRATE that equality should be false.
Are you using combineReducers from redux?
@rt2zz Yes, I am.
I have the same problem :/
@adambene @Grmiade sorry for the delay I would like to get to the bottom of this. I just added a test for what I thought you were describing (rehydrate fails with initial state) but the test passes. Can you share more details or some code from your implementation?
Even better if you can write a failing test!
Let me know, hoping to knock this out this week.
If my understanding is correct (and I'm basing it off this), !== doesn't work for comparing objects and so the test will always be true if the substate is an object.
All my substates are objects, so I got this to work by swapping out !== for a comparison using JSON.stringify. This post describes that and one other method for deep comparison of objects. I submitted a pull request (#245) with the change I made, but I have no idea if it's the best way to compare objects.
@jeffreycrow this leads me to believe to that you are mutating state by default in your reducer.
e.g. if you use switch statements the default case should always return state unmodified, i.e.
//...
default:
return state
This will preserve object equality without requiring deep compares
@rt2zz sorry to keep bothering you but i like this module :(
I keep getting this error as well but:
1) state is always cloned and never muted directly
2) state is always initialised
3) just routing is correctly rehyrdrated (even if not needed)
Console warning:
redux-persist/autoRehydrate: sub state for key `auth` modified, skipping autoRehydrate.
autoRehydrate.js:66 redux-persist/autoRehydrate: sub state for key `home` modified, skipping autoRehydrate.
autoRehydrate.js:66 redux-persist/autoRehydrate: sub state for key `item` modified, skipping autoRehydrate.
autoRehydrate.js:66 redux-persist/autoRehydrate: sub state for key `list` modified, skipping autoRehydrate.
store.js
const reducers = {
routing: routerReducer,
auth: authReducer,
home: homeReducer,
item: itemReducer,
list: listReducer,
};
const finalReducer = combineReducers(reducers);
const loggerMiddleware = createLogger();
const finalMiddleware = applyMiddleware(
thunkMiddleware,
promiseMiddleware,
loggerMiddleware,
routerMiddleware(browserHistory),
createActionBuffer(REHYDRATE),
);
const store = createStore(finalReducer, composeWithDevTools(
finalMiddleware,
autoRehydrate({ log: true }),
));
export default store;
auth.reducer.js
const authState = {
loading: false,
success: false,
error: false,
user: null,
group: null,
};
const authReducer = (previousState = authState, action) => {
const state = _.cloneDeep(previousState);
let type = action.type;
let payload = action.payload;
let key = action.key;
let value = action.value;
switch(type) {
case SET:
state[key] = value;
break;
case SIGN_IN:
state.user = value;
break;
case SIGN_OUT:
state.user = null;
break;
default:
break;
}
return state;
};
export default authReducer;
@damianobarbati ahah, so your problem is const state = _.cloneDeep(previousState);
I would rewrite your reducer as such:
const authState = {
loading: false,
success: false,
error: false,
user: null,
group: null,
};
const authReducer = (previousState = authState, action) => {
const state = _.cloneDeep(previousState);
let type = action.type;
let payload = action.payload;
let key = action.key;
let value = action.value;
switch(type) {
case SET:
state[key] = value;
return state;
break;
case SIGN_IN:
state.user = value;
return state;
break;
case SIGN_OUT:
state.user = null;
return state;
break;
default:
return previousState
break;
}
};
export default authReducer;
Basically you need to preserve object equality in the default case where state is not modified.
Let me know if that works.
@rt2zz solved doing as you said, thanks.
馃憤
Your Recommended Additions(set config.debug = true to get useful logging ) is very useful. I could not find my issue until open debug mode. Thanks, it's great.
Most helpful comment
@jeffreycrow this leads me to believe to that you are mutating state by default in your reducer.
e.g. if you use switch statements the default case should always return state unmodified, i.e.
This will preserve object equality without requiring deep compares