How to reset the state of a Redux store?

前端 未结 30 1903
陌清茗
陌清茗 2020-11-22 06:20

I am using Redux for state management.
How do I reset the store to its initial state?

For example, let’s say I have two user accounts (u1 and

相关标签:
30条回答
  • 2020-11-22 06:23

    One way to do that would be to write a root reducer in your application.

    The root reducer would normally delegate handling the action to the reducer generated by combineReducers(). However, whenever it receives USER_LOGOUT action, it returns the initial state all over again.

    For example, if your root reducer looked like this:

    const rootReducer = combineReducers({
      /* your app’s top-level reducers */
    })
    

    You can rename it to appReducer and write a new rootReducer delegating to it:

    const appReducer = combineReducers({
      /* your app’s top-level reducers */
    })
    
    const rootReducer = (state, action) => {
      return appReducer(state, action)
    }
    

    Now we just need to teach the new rootReducer to return the initial state after USER_LOGOUT action. As we know, reducers are supposed to return the initial state when they are called with undefined as the first argument, no matter the action. Let’s use this fact to conditionally strip the accumulated state as we pass it to appReducer:

     const rootReducer = (state, action) => {
      if (action.type === 'USER_LOGOUT') {
        state = undefined
      }
    
      return appReducer(state, action)
    }
    

    Now, whenever USER_LOGOUT fires, all reducers will be initialized anew. They can also return something different than they did initially if they want to because they can check action.type as well.

    To reiterate, the full new code looks like this:

    const appReducer = combineReducers({
      /* your app’s top-level reducers */
    })
    
    const rootReducer = (state, action) => {
      if (action.type === 'USER_LOGOUT') {
        state = undefined
      }
    
      return appReducer(state, action)
    }
    

    Note that I’m not mutating the state here, I am merely reassigning the reference of a local variable called state before passing it to another function. Mutating a state object would be a violation of Redux principles.

    In case you are using redux-persist, you may also need to clean your storage. Redux-persist keeps a copy of your state in a storage engine, and the state copy will be loaded from there on refresh.

    First, you need to import the appropriate storage engine and then, to parse the state before setting it to undefined and clean each storage state key.

    const rootReducer = (state, action) => {
        if (action.type === SIGNOUT_REQUEST) {
            // for all keys defined in your persistConfig(s)
            storage.removeItem('persist:root')
            // storage.removeItem('persist:otherKey')
    
            state = undefined;
        }
        return appReducer(state, action);
    };
    
    0 讨论(0)
  • 2020-11-22 06:25

    The accepted answer helped me solve my case. However, I encountered case where not-the-whole-state had to be cleared. So - I did it this way:

    const combinedReducer = combineReducers({
        // my reducers 
    });
    
    const rootReducer = (state, action) => {
        if (action.type === RESET_REDUX_STATE) {
            // clear everything but keep the stuff we want to be preserved ..
            delete state.something;
            delete state.anotherThing;
        }
        return combinedReducer(state, action);
    }
    
    export default rootReducer;
    

    Hope this helps someone else :)

    0 讨论(0)
  • 2020-11-22 06:26

    Combining the approaches of Dan, Ryan and Rob, to account for keeping the router state and initializing everything else in the state tree, I ended up with this:

    const rootReducer = (state, action) => appReducer(action.type === LOGOUT ? {
        ...appReducer({}, {}),
        router: state && state.router || {}
      } : state, action);
    
    0 讨论(0)
  • 2020-11-22 06:26

    In addition to Dan Abramov's answer, shouldn't we explicitly set action as action = {type: '@@INIT'} alongside state = undefined. With above action type, every reducer returns the initial state.

    0 讨论(0)
  • 2020-11-22 06:27

    in server, i have a variable is: global.isSsr = true and in each reducer, i have a const is : initialState To reset the data in the Store, I do the following with each Reducer: example with appReducer.js:

     const initialState = {
        auth: {},
        theme: {},
        sidebar: {},
        lsFanpage: {},
        lsChatApp: {},
        appSelected: {},
    };
    
    export default function (state = initialState, action) {
        if (typeof isSsr!=="undefined" && isSsr) { //<== using global.isSsr = true
            state = {...initialState};//<= important "will reset the data every time there is a request from the client to the server"
        }
        switch (action.type) {
            //...other code case here
            default: {
                return state;
            }
        }
    }
    

    finally on the server's router:

    router.get('*', (req, res) => {
            store.dispatch({type:'reset-all-blabla'});//<= unlike any action.type // i use Math.random()
            // code ....render ssr here
    });
    
    0 讨论(0)
  • 2020-11-22 06:29

    I have created actions to clear state. So when I dispatch a logout action creator I dispatch actions to clear state as well.

    User record action

    export const clearUserRecord = () => ({
      type: CLEAR_USER_RECORD
    });
    

    Logout action creator

    export const logoutUser = () => {
      return dispatch => {
        dispatch(requestLogout())
        dispatch(receiveLogout())
        localStorage.removeItem('auth_token')
        dispatch({ type: 'CLEAR_USER_RECORD' })
      }
    };
    

    Reducer

    const userRecords = (state = {isFetching: false,
      userRecord: [], message: ''}, action) => {
      switch (action.type) {
        case REQUEST_USER_RECORD:
        return { ...state,
          isFetching: true}
        case RECEIVE_USER_RECORD:
        return { ...state,
          isFetching: false,
          userRecord: action.user_record}
        case USER_RECORD_ERROR:
        return { ...state,
          isFetching: false,
          message: action.message}
        case CLEAR_USER_RECORD:
        return {...state,
          isFetching: false,
          message: '',
          userRecord: []}
        default:
          return state
      }
    };
    

    I am not sure if this is optimal?

    0 讨论(0)
提交回复
热议问题