Javascript 如何重置 Redux 商店的状态?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35622588/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:01:21  来源:igfitidea点击:

How to reset the state of a Redux store?

javascriptreduxstoreredux-store

提问by xyz

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

我正在使用 Redux 进行状态管理。
如何将商店重置为初始状态?

For example, let's say I have two user accounts (u1and u2).
Imagine the following sequence of events:

例如,假设我有两个用户帐户 (u1u2)。
想象以下事件序列:

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

  2. User u1logs out.

  3. User u2logs into the app without refreshing the browser.

  1. 用户u1登录应用程序并执行某些操作,因此我们在商店中缓存了一些数据。

  2. 用户u1注销。

  3. 用户u2无需刷新浏览器即可登录应用程序。

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

此时,缓存的数据将与 相关联u1,我想将其清理干净。

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

当第一个用户注销时,如何将 Redux 存储重置为其初始状态?

回答by Dan Abramov

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_LOGOUTaction, it returns the initial state all over again.

根减速器通常会将处理操作委托给由 生成的减速器combineReducers()。然而,每当它接收到USER_LOGOUT动作时,它都会重新返回初始状态。

For example, if your root reducer looked like this:

例如,如果您的根减速器如下所示:

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

You can rename it to appReducerand write a new rootReducerdelegating to it:

您可以将其重命名为appReducer并为其编写新的rootReducer委托:

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 rootReducerto return the initial state after USER_LOGOUTaction. As we know, reducers are supposed to return the initial state when they are called with undefinedas the first argument, no matter the action. Let's use this fact to conditionally strip the accumulated stateas we pass it to appReducer:

现在我们只需要教新人rootReducerUSER_LOGOUT行动后返回初始状态。正如我们所知,undefined无论操作如何,reducer 在被调用时都应该返回初始状态作为第一个参数。state当我们将它传递给 时,让我们使用这个事实来有条件地剥离累积appReducer

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

  return appReducer(state, action)
}

Now, whenever USER_LOGOUTfires, 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.typeas well.

现在,每当USER_LOGOUT触发时,所有减速器都将重新初始化。如果他们愿意,他们也可以返回与最初不同的东西,因为他们也可以检查action.type

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 referenceof a local variable called statebefore passing it to another function. Mutating a state object would be a violation of Redux principles.

请注意,我没有改变这里的状态,我只是在将它传递给另一个函数之前重新分配调用的局部变量的引用state。改变状态对象将违反 Redux 原则。

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.

如果您使用的是redux-persist,您可能还需要清理您的存储。Redux-persist 在存储引擎中保留一份你的状态副本,状态副本将在刷新时从那里加载。

First, you need to import the appropriate storage engineand then, to parse the state before setting it to undefinedand clean each storage state key.

首先,您需要导入适当的存储引擎,然后在设置之前解析状态undefined并清理每个存储状态键。

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);
};

回答by Ryan Irilli

I'd like to point out that the accepted comment by Dan Abramov is correct except we experienced a strange issue when using the react-router-redux package along with this approach. Our fix was to not set state to undefinedbut rather still use the current routing reducer. So I would suggest implementing the solution below if you are using this package

我想指出 Dan Abramov 接受的评论是正确的,除非我们在使用 react-router-redux 包和这种方法时遇到了一个奇怪的问题。我们的修复是不设置状态,undefined而是仍然使用当前的路由减少器。因此,如果您正在使用此软件包,我建议您实施以下解决方案

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

回答by nirbhaygp

Define an action:

定义一个动作:

const RESET_ACTION = {
  type: "RESET"
}

Then in each of your reducers assuming you are using switchor if-elsefor handling multiple actions through each reducer. I am going to take the case for a switch.

然后在假设您正在使用switchif-else通过每个减速器处理多个操作的每个减速器中。我要为一个switch.

const INITIAL_STATE = {
  loggedIn: true
}

const randomReducer = (state=INITIAL_STATE, action) {
  switch(action.type) {
    case 'SOME_ACTION_TYPE':

       //do something with it

    case "RESET":

      return INITIAL_STATE; //Always return the initial state

   default: 
      return state; 
  }
}

This way whenever you call RESETaction, you reducer will update the store with default state.

这样,无论何时调用RESETaction,reducer 都会用默认状态更新 store。

Now, for logout you can handle the like below:

现在,对于注销,您可以处理如下:

const logoutHandler = () => {
    store.dispatch(RESET_ACTION)
    // Also the custom logic like for the rest of the logout handler
}

Every time a userlogs in, without a browser refresh. Store will always be at default.

每次用户登录时,无需刷新浏览器。商店将始终处于默认状态。

store.dispatch(RESET_ACTION)just elaborates the idea. You will most likely have an action creator for the purpose. A much better way will be that you have a LOGOUT_ACTION.

store.dispatch(RESET_ACTION)只是阐述了这个想法。为此,您很可能会有一个动作创建者。更好的方法是你有一个LOGOUT_ACTION.

Once you dispatch this LOGOUT_ACTION. A custom middleware can then intercept this action, either with Redux-Saga or Redux-Thunk. Both ways however, you can dispatch another action 'RESET'. This way store logout and reset will happen synchronously and your store will ready for another user login.

一旦你派遣这个LOGOUT_ACTION. 然后,自定义中间件可以使用 Redux-Saga 或 Redux-Thunk 拦截此操作。但是,这两种方式都可以发送另一个动作“RESET”。这样,商店注销和重置将同步发生,您的商店将为另一个用户登录做好准备。

回答by Matt Carlotta

Just a simplified answer to the best answer:

只是最佳答案的简化答案:

const rootReducer = combineReducers({
    auth: authReducer,
    ...formReducers,
    routing
});


export default (state, action) =>
  rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);

回答by Daniel petrov

 const reducer = (state = initialState, { type, payload }) => {

   switch (type) {
      case RESET_STORE: {
        state = initialState
      }
        break
   }

   return state
 }

You can also fire an action which is handled by all or some reducers, that you want to reset to initial store. One action can trigger a reset to your whole state, or just a piece of it that seems fit to you. I believe this is the simplest and most controllable way of doing this.

您还可以触发由所有或某些减速器处理的操作,您希望将其重置为初始存储。一个动作可以触发整个状态的重置,或者只是其中一部分似乎适合你。我相信这是最简单和最可控的方法。

回答by Rob Moorman

With Redux if have applied the following solution, which assumes I have set an initialState in all my reducers (e.g. { user: { name, email }}). In many components I check on these nested properties, so with this fix I prevent my renders methods are broken on coupled property conditions (e.g. if state.user.email, which will throw an error user is undefined if upper mentioned solutions).

对于 Redux,如果应用了以下解决方案,假设我已经在所有减速器中设置了一个初始状态(例如 { user: { name, email }})。在许多组件中,我检查了这些嵌套属性,因此通过此修复,我可以防止我的渲染方法在耦合属性条件下被破坏(例如,如果 state.user.email,如果上面提到的解决方案将抛出错误 user is undefined)。

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

const rootReducer = (state, action) => {
  if (action.type === 'LOG_OUT') {
    state = initialState
  }

  return appReducer(state, action)
}

回答by Tyler Brown

UPDATE NGRX4

更新 NGRX4

If you are migrating to NGRX 4, you may have noticed from the migration guidethat the rootreducer method for combining your reducers has been replaced with ActionReducerMap method. At first, this new way of doing things might make resetting state a challenge. It is actually straight-forward, yet the way of doing this has changed.

如果您要迁移到 NGRX 4,您可能已经从迁移指南中注意到,用于组合您的减速器的 rootreducer 方法已被 ActionReducerMap 方法取代。起初,这种新的做事方式可能会给重置状态带来挑战。它实际上是直截了当的,但这样做的方式已经改变。

This solution is inspired by the meta-reducers API section of the NGRX4 Github docs.

该解决方案的灵感来自NGRX4 Github 文档的元减速器 API 部分

First, lets say your are combining your reducers like this using NGRX's new ActionReducerMap option:

首先,假设您正在使用 NGRX 的新 ActionReducerMap 选项组合这样的减速器:

//index.reducer.ts
export const reducers: ActionReducerMap<State> = {
    auth: fromAuth.reducer,
    layout: fromLayout.reducer,
    users: fromUsers.reducer,
    networks: fromNetworks.reducer,
    routingDisplay: fromRoutingDisplay.reducer,
    routing: fromRouting.reducer,
    routes: fromRoutes.reducer,
    routesFilter: fromRoutesFilter.reducer,
    params: fromParams.reducer
}

Now, lets say you want to reset state from within app.module `

现在,假设您想从 app.module 中重置状态`

//app.module.ts
import { IndexReducer } from './index.reducer';
import { StoreModule, ActionReducer, MetaReducer } from '@ngrx/store';
...
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
    return function(state, action) {

      switch (action.type) {
          case fromAuth.LOGOUT:
            console.log("logout action");
            state = undefined;
      }

      return reducer(state, action);
    }
  }

  export const metaReducers: MetaReducer<any>[] = [debug];

  @NgModule({
    imports: [
        ...
        StoreModule.forRoot(reducers, { metaReducers}),
        ...
    ]
})

export class AppModule { }

`

`

And that is basically one way to achieve the same affect with NGRX 4.

这基本上是使用 NGRX 4 实现相同效果的一种方法。

回答by Andy_D

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

结合 Dan、Ryan 和 Rob 的方法,为了保持router状态并初始化状态树中的所有其他内容,我最终得到了这样的结果:

const rootReducer = (state, action) => appReducer(action.type === LOGOUT ? {
    ...appReducer({}, {}),
    router: state && state.router || {}
  } : state, action);

回答by Navinesh Chand

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?

我不确定这是否最佳?

回答by wwayne

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.

我已经创建了一个组件来给 Redux 重置状态的能力,你只需要使用这个组件来增强你的商店并调度一个特定的 action.type 来触发重置。实现的想法与@Dan Abramov 所说的相同。

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

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