Reputation: 2961
I am trying to logout and purge the store at the same time, so on click I dispatch this:
dispatch({type: PURGE, key: 'root', result: () => { } });
Redux persist catches it, and reports purging the store. Great. In another reducer I catch that dispatch, and remove my access token like this:
import { PURGE } from 'redux-persist/es/constants';
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setAccessToken(state: AuthState, action: PayloadAction<Auth>): void {
state.accessToken = action.payload.accessToken;
state.expiresIn = action.payload.expiresIn;
},
},
extraReducers: {
[PURGE]: (state: AuthState, action: string): void => {
state.accessToken = initialState.accessToken;
state.expiresIn = initialState.expiresIn;
},
},
});
The PURGE reducer actually is called, and modifies the state, but still no re-rendering happens. so redux must not pick that up. But according to the docs the Redux toolkit uses a Proxy object for the state and does a comparison to see if it's modified.
Things I tried:
state = initialState;
and
state = { ...initialState };
Didn't work. The store works, and holds data, other actions work. How do I proceed?
EDIT: Further debugging revealed that my own reducer was called BEFORE the redux-persist reducer, and redux-logger reported that my reducer did not change the state at all.
Upvotes: 2
Views: 4635
Reputation: 101
I'm facing a similar issue (not re-rendering) and came by this thread today: Seems like you can't replace state objects entirely.
From: https://redux-toolkit.js.org/usage/immer-reducers
Sometimes you may want to replace the entire existing state, either because you've loaded some new data, or you want to reset the state back to its initial value.
WARNING A common mistake is to try assigning state = someValue directly. This will not work! This only points the local state variable to a different reference. That is neither mutating the existing state object/array in memory, nor returning an entirely new value, so Immer does not make any actual changes.
const initialState = []
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
brokenTodosLoadedReducer(state, action) {
// ❌ ERROR: does not actually mutate or return anything new!
state = action.payload
},
fixedTodosLoadedReducer(state, action) {
// ✅ CORRECT: returns a new value to replace the old one
return action.payload
},
correctResetTodosReducer(state, action) {
// ✅ CORRECT: returns a new value to replace the old one
return initialState
},
},
})
So
state = initialState;
would be
return initialState;
Upvotes: 5
Reputation: 2961
This turned out to be the solution:
extraReducers: {
[PURGE]: (state: UserState, action: string): UserState => ({
...state,
...initialState,
}),
},
I don't understand why, as modifying the state object should work too, according to the documentation:
To make things easier, createReducer uses immer to let you write reducers as if they were mutating the state directly. In reality, the reducer receives a proxy state that translates all mutations into equivalent copy operations.
Upvotes: 1