How to reset the state of a Redux store?

2019-01-01 01: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 u2).
Imagine the following sequence of events:

  1. User u1 logs into the app and does something, so we cache some data in the store.

  2. User u1 logs out.

  3. User u2 logs into the app without refreshing the browser.

At this point, the cached data will be associated with u1, and I would like to clean it up.

How can I reset the Redux store to its initial state when the first user logs out?

24条回答
弹指情弦暗扣
2楼-- · 2019-01-01 01:59

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?

查看更多
有味是清欢
3楼-- · 2019-01-01 02:00

I found that the accepted answer worked well for me, but it triggered the ESLint no-param-reassign error - https://eslint.org/docs/rules/no-param-reassign

Here's how I handled it instead, making sure to create a copy of the state (which is, in my understanding, the Reduxy thing to do...):

import { combineReducers } from "redux"
import { routerReducer } from "react-router-redux"
import ws from "reducers/ws"
import session from "reducers/session"
import app from "reducers/app"

const appReducer = combineReducers({
    "routing": routerReducer,
    ws,
    session,
    app
})

export default (state, action) => {
    const stateCopy = action.type === "LOGOUT" ? undefined : { ...state }
    return appReducer(stateCopy, action)
}

But maybe creating a copy of the state to just pass it into another reducer function that creates a copy of that is a little over-complicated? This doesn't read as nicely, but is more to-the-point:

export default (state, action) => {
    return appReducer(action.type === "LOGOUT" ? undefined : state, action)
}
查看更多
梦寄多情
4楼-- · 2019-01-01 02:01

why don't you just use return module.exports.default() ;)

export default (state = {pending: false, error: null}, action = {}) => {
    switch (action.type) {
        case "RESET_POST":
            return module.exports.default();
        case "SEND_POST_PENDING":
            return {...state, pending: true, error: null};
        // ....
    }
    return state;
}

Note: make sure you set action default value to {} and you are ok because you don't want to encounter error when you check action.type inside the switch statement.

查看更多
旧人旧事旧时光
5楼-- · 2019-01-01 02:03

This approach is very right: Destruct any specific state "NAME" to ignore and keep others.

const rootReducer = (state, action) => {
    if (action.type === 'USER_LOGOUT') {
        state.NAME = undefined
    }
    return appReducer(state, action)
}
查看更多
裙下三千臣
6楼-- · 2019-01-01 02:04

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 of using redux-persist, you may also need to clean your storage. Redux-persist keeps a copy of your state in a storage engine, and they 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) {
        Object.keys(state).forEach(key => {
            storage.removeItem(`persist:${key}`);
        });
        state = undefined;
    }
    return AppReducers(state, action);
};
查看更多
回忆,回不去的记忆
7楼-- · 2019-01-01 02:04

I've created a component to give Redux the ability of resetting state, you just need to use this component to enhance your store and dispatch a specific action.type to trigger reset. The thought of implementation is same as what @Dan Abramov said.

Github: https://github.com/wwayne/redux-reset

查看更多
登录 后发表回答