简体   繁体   English

如何重置 Redux 商店的状态?

[英]How to reset the state of a Redux store?

I am using Redux for state management.我正在使用 Redux 进行状态管理。
How do I reset the store to its initial state?如何将商店重置为初始状态?

For example, let's say I have two user accounts ( u1 and u2 ).例如,假设我有两个用户帐户( u1u2 )。
Imagine the following sequence of events:想象以下事件序列:

  1. User u1 logs into the app and does something, so we cache some data in the store.用户u1登录应用程序并执行某些操作,因此我们在商店中缓存了一些数据。

  2. User u1 logs out.用户u1注销。

  3. User u2 logs into the app without refreshing the browser.用户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 存储重置为其初始状态?

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() .根减速器通常会将处理操作委托给由combineReducers()生成的减速器。 However, whenever it receives USER_LOGOUT action, it returns the initial state all over again.但是,每当它接收到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 appReducer and write a new rootReducer delegating 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 rootReducer to return the initial state in response to the USER_LOGOUT action.现在我们只需要教新的rootReducer返回初始状态以响应USER_LOGOUT操作。 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.正如我们所知,无论操作如何,当使用undefined作为第一个参数调用 reducer 时,它们都应该返回初始状态。 Let's use this fact to conditionally strip the accumulated state as we pass it to appReducer :当我们将它传递给appReducer让我们使用这个事实来有条件地剥离累积state

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

  return appReducer(state, action)
}

Now, whenever USER_LOGOUT fires, all reducers will be initialized anew.现在,每当USER_LOGOUT触发时,所有减速器都将重新初始化。 They can also return something different than they did initially if they want to because they can check action.type as well.如果他们愿意,他们也可以返回与最初不同的东西,因为他们也可以检查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') {
    return appReducer(undefined, action)
  }

  return appReducer(state, action)
}

In case you are using redux-persist , you may also need to clean your storage.如果您使用的是redux-persist ,您可能还需要清理您的存储。 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 在存储引擎中保留一份状态副本,状态副本将在刷新时从那里加载。

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.首先,您需要导入适当的存储引擎,然后在将其设置为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')

        return appReducer(undefined, action);
    }
    return appReducer(state, action);
};

Dan Abramov 's answer is correct except we experienced a strange issue when using the react-router-redux package along with this approach. Dan Abramov回答是正确的,只是我们在使用 react-router-redux 包和这种方法时遇到了一个奇怪的问题。

Our fix was to not set the state to undefined but rather still use the current routing reducer.我们的修复是不将状态设置为undefined ,而是仍然使用当前的路由减少器。 So I would suggest implementing the solution below if you are using this package因此,如果您正在使用此软件包,我建议您实施以下解决方案

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

Define an action:定义一个动作:

const RESET_ACTION = {
  type: "RESET"
}

Then in each of your reducers assuming you are using switch or if-else for handling multiple actions through each reducer.然后在每个减速器中假设您使用switchif-else通过每个减速器处理多个动作。 I am going to take the case for a switch .我要拿这个案子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 RESET action, you reducer will update the store with default state.这样,每当您调用RESET操作时,您的减速器都会使用默认状态更新存储。

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. store.dispatch(RESET_ACTION)只是阐述了这个想法。 You will most likely have an action creator for the purpose.为此,您很可能会有一个动作创建者。 A much better way will be that you have a LOGOUT_ACTION .更好的方法是你有一个LOGOUT_ACTION

Once you dispatch this LOGOUT_ACTION .一旦你发送了这个LOGOUT_ACTION A custom middleware can then intercept this action, either with Redux-Saga or Redux-Thunk.然后,自定义中间件可以使用 Redux-Saga 或 Redux-Thunk 拦截此操作。 Both ways however, you can dispatch another action 'RESET'.但是,这两种方式都可以发送另一个动作“RESET”。 This way store logout and reset will happen synchronously and your store will ready for another user login.这样,商店注销和重置将同步发生,您的商店将为另一个用户登录做好准备。

Just a simplified answer to Dan Abramov 's answer :只是对Dan Abramov回答的简化回答

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


export default (state, action) =>
  rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);
 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.我相信这是最简单和最可控的方法。

From a security perspective, the safest thing to do when logging a user out is to reset all persistent state (ex cookies, localStorage , IndexedDB , Web SQL , etc) and do a hard refresh of the page using window.location.reload() .从安全角度来看,注销用户时最安全的做法是重置所有持久状态(例如 cookie、 localStorageIndexedDBWeb SQL等)并使用window.location.reload()对页面进行硬刷新. It's possible a sloppy developer accidentally or intentionally stored some sensitive data on window , in the DOM, etc. Blowing away all persistent state and refreshing the browser is the only way to guarantee no information from the previous user is leaked to the next user.粗心的开发者可能会无意或故意将一些敏感数据存储在window 、 DOM 等中。吹掉所有持久状态并刷新浏览器是保证前一个用户的信息不会泄露给下一个用户的唯一方法。

(Of course, as a user on a shared computer you should use "private browsing" mode, close the browser window yourself, use the "clear browsing data" function, etc, but as a developer we can't expect everyone to always be that diligent) (当然,作为共享电脑的用户,你应该使用“隐私浏览”模式,自己关闭浏览器窗口,使用“清除浏览数据”功能等,但作为开发者我们不能期望每个人总是那个勤奋)

With Redux if have applied the following solution, which assumes I have set an initialState in all my reducers (eg { user: { name, email }} ).对于 Redux,如果应用了以下解决方案,假设我已经在所有减速器中设置了一个initialState (例如{ 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 (eg if state.user.email , which will throw an error user is undefined if the upper mentioned solutions).在我检查这些嵌套属性的许多组件中,因此通过此修复程序,我可以防止我的渲染方法在耦合属性条件下被破坏(例如,如果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)
}

Using Redux Toolkit and/or Typescript :使用Redux Toolkit和/或Typescript

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

const rootReducer = (
  state: ReturnType<typeof appReducer>,
  action: AnyAction
) => {
/* if you are using RTK, you can import your action and use it's type property instead of the literal definition of the action  */
  if (action.type === logout.type) {
    return appReducer(undefined, { type: undefined });
  }

  return appReducer(state, action);
};

UPDATE NGRX4更新 NGRX4

If you are migrating to NGRX 4, you may have noticed from the migration guide that the rootreducer method for combining your reducers has been replaced with the ActionReducerMap method.如果您要迁移到 NGRX 4,您可能已经从迁移指南中注意到用于组合您的减速器的rootreducer方法已被ActionReducerMap方法取代。 At first, this new way of doing things might make resetting the state a challenge.起初,这种新的做事方式可能会使重置状态成为一项挑战。 It is actually straightforward, yet the way of doing this has changed.这实际上很简单,但这样做的方式已经改变。

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, let's say you want to reset the 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 实现相同效果的一种方法。

My workaround when working with typescript, built on top of Dan Abramov 's answer (redux typings make it impossible to pass undefined to reducer as the first argument, so I cache initial root state in a constant):我在使用打字稿时的解决方法,建立在Dan Abramov答案之上(redux 类型使得不可能将undefined作为第一个参数传递给 reducer,所以我将初始根状态缓存在一个常量中):

// store

export const store: Store<IStoreState> = createStore(
  rootReducer,
  storeEnhacer,
)

export const initialRootState = {
  ...store.getState(),
}

// root reducer

const appReducer = combineReducers<IStoreState>(reducers)

export const rootReducer = (state: IStoreState, action: IAction<any>) => {
  if (action.type === "USER_LOGOUT") {
    return appReducer(initialRootState, action)
  }

  return appReducer(state, action)
}


// auth service

class Auth {
  ...

  logout() {
    store.dispatch({type: "USER_LOGOUT"})
  }
}

Combining Dan Abramov 's answer , Ryan Irilli 's answer and Rob Moorman 's answer , to account for keeping the router state and initializing everything else in the state tree, I ended up with this:结合Dan Abramov回答Ryan Irilli回答Rob Moorman回答,为了保持router状态并初始化状态树中的所有其他内容,我最终得到了这个:

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

Simply have your logout link clear session and refresh the page.只需让您的注销链接清除会话并刷新页面即可。 No additional code needed for your store.您的商店不需要额外的代码。 Any time you want to completely reset the state a page refresh is a simple and easily repeatable way to handle it.任何时候您想要完全重置状态,页面刷新都是一种简单且易于重复的处理方式。

I've created a component to give Redux the ability to reset state, you just need to use this component to enhance your store and dispatch a specific action.type to trigger reset.我创建了一个组件来让 Redux 能够重置状态,你只需要使用这个组件来增强你的商店并调度一个特定的action.type来触发重置。 The thought of implementation is the same as what Dan Abramov said in their answer .实施的想法与Dan Abramov在他们的回答中所说的相同。

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

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?我不确定这是否最佳?

My take to keep Redux from referencing to the same variable of the initial state:我要防止 Redux 引用初始状态的相同变量:

// write the default state as a function
const defaultOptionsState = () => ({
  option1: '',
  option2: 42,
});

const initialState = {
  options: defaultOptionsState() // invoke it in your initial state
};

export default (state = initialState, action) => {

  switch (action.type) {

    case RESET_OPTIONS:
    return {
      ...state,
      options: defaultOptionsState() // invoke the default function to reset this part of the state
    };

    default:
    return state;
  }
};

If you are using redux-actions , here's a quick workaround using a HOF( Higher Order Function ) for handleActions .如果您使用的是redux-actions ,这里有一个使用 HOF(高阶函数)作为handleActions的快速解决方法。

import { handleActions } from 'redux-actions';

export function handleActionsEx(reducer, initialState) {
  const enhancedReducer = {
    ...reducer,
    RESET: () => initialState
  };
  return handleActions(enhancedReducer, initialState);
}

And then use handleActionsEx instead of original handleActions to handle reducers.然后使用handleActionsEx而不是原始的handleActions来处理减速器。

Dan's answer gives a great idea about this problem, but it didn't work out well for me, because I'm using redux-persist . Dan 的回答对这个问题给出了一个很好的主意,但对我来说效果不佳,因为我使用的是redux-persist
When used with redux-persist , simply passing undefined state didn't trigger persisting behavior, so I knew I had to manually remove item from storage (React Native in my case, thus AsyncStorage ).当与redux-persist ,简单地传递undefined状态不会触发持久化行为,所以我知道我必须手动从存储中删除项目(在我的情况下是 React Native,因此是AsyncStorage )。

await AsyncStorage.removeItem('persist:root');

or或者

await persistor.flush(); // or await persistor.purge();

didn't work for me either - they just yelled at me.对我也不起作用——他们只是冲我大喊大叫。 (eg, complaining like "Unexpected key _persist ..." ) (例如,像"Unexpected key _persist ..."这样的抱怨)

Then I suddenly pondered all I want is just make every individual reducer return their own initial state when RESET action type is encountered.然后我突然想到我想要的只是让每个减速器在遇到RESET操作类型时返回自己的初始状态。 That way, persisting is handled naturally.这样,持久化就自然而然地处理了。 Obviously without above utility function ( handleActionsEx ), my code won't look DRY (although it's just a one liner, ie RESET: () => initialState ), but I couldn't stand it 'cuz I love metaprogramming.很明显,如果没有上面的实用函数( handleActionsEx ),我的代码看起来不会很干(虽然它只是一个单行代码,即RESET: () => initialState ),但我无法忍受,因为我喜欢元编程。

The following solution worked for me.以下解决方案对我有用。

I added resetting state function to meta reducers.The key was to use我在元减速器中添加了重置状态功能。关键是要使用

return reducer(undefined, action);

to set all reducers to initial state.将所有减速器设置为初始状态。 Returning undefined instead was causing errors due to the fact that the structure of the store has been destroyed.由于存储的结构已被破坏,返回undefined会导致错误。

/reducers/index.ts /reducers/index.ts

export function resetState(reducer: ActionReducer<State>): ActionReducer<State> {
  return function (state: State, action: Action): State {

    switch (action.type) {
      case AuthActionTypes.Logout: {
        return reducer(undefined, action);
      }
      default: {
        return reducer(state, action);
      }
    }
  };
}

export const metaReducers: MetaReducer<State>[] = [ resetState ];

app.module.ts app.module.ts

import { StoreModule } from '@ngrx/store';
import { metaReducers, reducers } from './reducers';

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

Dan Abramov 's answer helped me solve my case. Dan Abramov回答帮助我解决了我的案子。 However, I encountered a case where not the entire 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;

Just an extension to @dan-abramov answer, sometimes we may need to retain certain keys from being reset.只是@dan-abramov答案的扩展,有时我们可能需要保留某些键不被重置。

const retainKeys = ['appConfig'];

const rootReducer = (state, action) => {
  if (action.type === 'LOGOUT_USER_SUCCESS' && state) {
    state = !isEmpty(retainKeys) ? pick(state, retainKeys) : undefined;
  }

  return appReducer(state, action);
};

A quick and easy option that worked for me was using redux-reset .对我有用的快速简便的选项是使用redux-reset Which was straightforward and also has some advanced options, for larger apps.对于较大的应用程序,这很简单,并且还有一些高级选项。

Setup in create store在创建商店中设置

import reduxReset from 'redux-reset'
// ...
const enHanceCreateStore = compose(
    applyMiddleware(...),
    reduxReset()  // Will use 'RESET' as default action.type to trigger reset
)(createStore)
const store = enHanceCreateStore(reducers)

Dispatch your 'reset' in your logout function在您的注销功能中发送您的“重置”

store.dispatch({
    type: 'RESET'
})

I found that Dan Abramov 's answer worked well for me, but it triggered the ESLint no-param-reassign error - https://eslint.org/docs/rules/no-param-reassign我发现Dan Abramov回答对我很有效,但它触发了 ESLint no-param-reassign错误 - 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...):这是我处理它的方式,确保创建状态的副本(在我看来,这是 Reduxy 要做的事情......):

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?但也许创建状态的副本以将其传递给另一个创建副本的 reducer 函数有点过于复杂? This doesn't read as nicely, but is more to-the-point:这读起来不太好,但更切中要害:

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

I am using Redux for state management.我正在使用Redux进行状态管理。
How do I reset the store to its initial state?如何将商店重置为初始状态?

For example, let's say I have two user accounts ( u1 and u2 ).例如,假设我有两个用户帐户( u1u2 )。
Imagine the following sequence of events:想象以下事件序列:

  1. User u1 logs into the app and does something, so we cache some data in the store.用户u1登录到该应用并执行某项操作,因此我们在存储中缓存了一些数据。

  2. User u1 logs out.用户u1注销。

  3. User u2 logs into the app without refreshing the browser.用户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存储重置为其初始状态?

I am using Redux for state management.我正在使用Redux进行状态管理。
How do I reset the store to its initial state?如何将商店重置为初始状态?

For example, let's say I have two user accounts ( u1 and u2 ).例如,假设我有两个用户帐户( u1u2 )。
Imagine the following sequence of events:想象以下事件序列:

  1. User u1 logs into the app and does something, so we cache some data in the store.用户u1登录到该应用并执行某项操作,因此我们在存储中缓存了一些数据。

  2. User u1 logs out.用户u1注销。

  3. User u2 logs into the app without refreshing the browser.用户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存储重置为其初始状态?

First on initiation of our application the reducer state is fresh and new with default InitialState .首先对我们的应用程序开始减速状态是耳目一新默认初始化状态

We have to add an action that calls on APP inital load to persists default state .我们必须添加一个调用 APP 初始加载的操作以保持默认状态

While logging out of the application we can simple reAssign the default state and reducer will work just as new .在注销应用程序时,我们可以简单地重新分配默认状态,reducer 将像new 一样工作。

Main APP Container主APP容器

  componentDidMount() {   
    this.props.persistReducerState();
  }

Main APP Reducer主APP减速机

const appReducer = combineReducers({
  user: userStatusReducer,     
  analysis: analysisReducer,
  incentives: incentivesReducer
});

let defaultState = null;
export default (state, action) => {
  switch (action.type) {
    case appActions.ON_APP_LOAD:
      defaultState = defaultState || state;
      break;
    case userLoginActions.USER_LOGOUT:
      state = defaultState;
      return state;
    default:
      break;
  }
  return appReducer(state, action);
};

On Logout calling action for resetting state在注销时调用用于重置状态的操作

function* logoutUser(action) {
  try {
    const response = yield call(UserLoginService.logout);
    yield put(LoginActions.logoutSuccess());
  } catch (error) {
    toast.error(error.message, {
      position: toast.POSITION.TOP_RIGHT
    });
  }
}

For me to reset the state to its initial state, I wrote the following code:为了将状态重置为初始状态,我编写了以下代码:

const appReducers = (state, action) =>
   combineReducers({ reducer1, reducer2, user })(
     action.type === "LOGOUT" ? undefined : state,
     action
);

One thing Dan Abramov 's answer doesn't do is clear the cache for parameterized selectors. Dan Abramov回答没有做的一件事是清除参数化选择器的缓存。 If you have a selector like this:如果您有这样的选择器:

export const selectCounter1 = (state: State) => state.counter1;
export const selectCounter2 = (state: State) => state.counter2;
export const selectTotal = createSelector(
  selectCounter1,
  selectCounter2,
  (counter1, counter2) => counter1 + counter2
);

Then you would have to release them on logout like this:然后你必须像这样在注销时释放它们:

selectTotal.release();

Otherwise, the memoized value for the last call of the selector and the values of the last parameters will still be in memory.否则,选择器最后一次调用的记忆值和最后一个参数的值仍将在内存中。

Code samples are from the ngrx docs .代码示例来自ngrx 文档

 onLogout() { this.props.history.push('/login'); // send user to login page window.location.reload(); // refresh the page } 

Approach with Redux Toolkit:使用 Redux Toolkit 的方法:


export const createRootReducer = (history: History) => {
  const rootReducerFn = combineReducers({
    auth: authReducer,
    users: usersReducer,
    ...allOtherReducers,
    router: connectRouter(history),
  });

  return (state: Parameters<typeof rootReducerFn>[0], action: Parameters<typeof rootReducerFn>[1]) =>
    rootReducerFn(action.type === appActions.reset.type ? undefined : state, action);
};

This approach is very right: Destruct any specific state "NAME" to ignore and keep others.这种方法非常正确:销毁任何特定状态“NAME”以忽略并保留其他状态。

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

why don't you just use return module.exports.default() ;)为什么不直接使用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.注意:确保您将 action 默认值设置为{}并且您没问题,因为您不想在 switch 语句中检查action.type时遇到错误。

Another option is to:另一种选择是:

store.dispatch({type: '@@redux/INIT'})

'@@redux/INIT' is the action type that redux dispatches automatically when you createStore , so assuming your reducers all have a default already, this would get caught by those and start your state off fresh. '@@redux/INIT'是当你createStore时 redux 自动调度的动作类型,所以假设你的 reducer 都已经有一个默认值,这会被那些捕获并重新启动你的状态。 It might be considered a private implementation detail of redux, though, so buyer beware...不过,它可能被认为是 redux 的私有实现细节,所以买家要小心......

for me what worked the best is to set the initialState instead of state :对我来说最有效的是设置initialState而不是state

  const reducer = createReducer(initialState,
  on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
    ...initialState
  })),

If you want to reset a single reducer如果要重置单个减速器

For example例如

 const initialState = { isLogged: false } //this will be your action export const resetReducer = () => { return { type: "RESET" } } export default (state = initialState, { type, payload }) => { switch (type) { //your actions will come her case "RESET": return { ...initialState } } } //and from your frontend dispatch(resetReducer())

You can null the reducers' data by adding this code to action file,您可以通过将此代码添加到操作文件中来清空减速器的数据,

import all types first:首先导入所有类型:

import * as types from './types';

add this code to logout action将此代码添加到注销操作

for(let key of Object.values(types)) {
        dispatch({ type: key, payload: [] });
    }
npm install redux-reset
import reduxReset from 'redux-reset'
...
const enHanceCreateStore = compose(
    applyMiddleware(...),
    reduxReset()  // Will use 'RESET' as default action.type to trigger reset
  )(createStore)
const store = enHanceCreateStore(reducers)

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

just edit the file where the reducers are declared只需编辑声明减速器的文件

import { combineReducers } from 'redux';

import gets from '../';

const rootReducer = (state, action) => {
  let asReset = action.type === 'RESET_STORE';

  const reducers = combineReducers({
    gets,
  });

  const transition = {
    true() {
      return reducers({}, action);
    },
    false() {
      return reducers(state, action);
    },
  };
  return transition[asReset] && transition[asReset]();
};

export default rootReducer;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM