使用 @reduxjs/toolkit 中的 configureStore 时如何重置 Redux Store 的状态?

Posted

技术标签:

【中文标题】使用 @reduxjs/toolkit 中的 configureStore 时如何重置 Redux Store 的状态?【英文标题】:How to reset state of Redux Store when using configureStore from @reduxjs/toolkit? 【发布时间】:2020-03-22 11:25:29 【问题描述】:

我看到了注销后清除/重置存储的解决方案,但不明白如何为以下设置 redux 存储的方式实现相同的功能。

Store.js:


import  configureStore, getDefaultMiddleware  from '@reduxjs/toolkit'
import authReducer from './ducks/authentication'
import snackbar from './ducks/snackbar'
import sidebar from './ducks/sidebar'
import global from './ducks/global'
import quickView from './ducks/quickView'
import profileView from './ducks/profileView'

const store = configureStore(
  reducer: 
    auth: authReducer,
    snackbar,
    sidebar,
    global,
    quickView,
    profileView,
  ,
  middleware: [...getDefaultMiddleware()],
)

export default store



这里是所有 reducer 如何使用来自 @reduxjs/toolkit 的 createAction 和 createReducer 实现的。

snackbar.js:


import  createAction, createReducer  from '@reduxjs/toolkit'

export const handleSnackbar = createAction('snackbar/handleSnackbar')

export const openSnackBar = (
  verticalPosition,
  horizontalPosition,
  message,
  messageType,
  autoHideDuration = 10000
) => 
  return async dispatch => 
    dispatch(
      handleSnackbar(
        verticalPosition,
        horizontalPosition,
        message,
        autoHideDuration,
        messageType,
        isOpen: true,
      )
    )
  


export const closeSnackbar = () => 
  return dispatch => 
    dispatch(handleSnackbar( isOpen: false ))
  


const initialState = 
  verticalPosition: 'bottom',
  horizontalPosition: 'center',
  message: '',
  autoHideDuration: 6000,
  isOpen: false,
  messageType: 'success',


export default createReducer(initialState, 
  [handleSnackbar]: (state, action) => 
    const 
      isOpen,
      verticalPosition,
      horizontalPosition,
      message,
      autoHideDuration,
      messageType,
     = action.payload
    state.isOpen = isOpen
    state.verticalPosition = verticalPosition
    state.horizontalPosition = horizontalPosition
    state.message = message
    state.autoHideDuration = autoHideDuration
    state.messageType = messageType
  ,
)



【问题讨论】:

你派发一个 thunk 动作,该动作派发一个 reset() 动作到每个 reducer,触发它们返回初始状态。 嗨@timotgl,非常感谢您的回复。你能分享一个例子sn-p吗?那真的很有帮助。 见下方答案 【参考方案1】:

根据Dan Abramov's answer,创建一个root reducer,它将简单地将action 委托给您的ma​​in 或组合reducer。每当这个根 reducer 收到 reset 类型的操作时,它都会重置状态。

示例:

const combinedReducer = combineReducers(
  first: firstReducer,
  second: secondReducer,
  // ... all your app's reducers
)

const rootReducer = (state, action) => 
  if (action.type === 'RESET') 
    state = undefined
  
  return combinedReducer(state, action)

因此,如果您已使用 @reduxjs/toolkit's configureStore 配置您的商店,它可能如下所示:

import  configureStore  from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

export default configureStore(
  reducer: 
    counter: counterReducer,
    // ... more reducers
  ,
);

其中configureStore 的第一个参数reducer 接受一个函数(被视为根减速器)或slice 减速器的对象,它在内部使用 combineReducers 转换为 root reducer

所以,现在我们可以自己创建并传递根 reducer,而不是传递 slice reducer 的对象(如上所示),我们可以这样做:

const combinedReducer = combineReducers(
  counter: counterReducer,
  // ... more reducers
);

现在,让我们创建一个根减速器,它会在需要时完成我们的重置工作:

const rootReducer = (state, action) => 
  if (action.type === 'counter/logout')  // check for action type 
    state = undefined;
  
  return combinedReducer(state, action);
;

export default configureStore(
  reducer: rootReducer,
  middleware: [...getDefaultMiddleware()]
);

这里是CodeSandbox

【讨论】:

这个答案应该在 redux-toolkit 的文档中,谢谢! P.S:看看 CodeSandbox 的实现,Ajeet 在那里添加了更多的 cmets。 有类型的 CodeSandbox 的 TypeScript 版本的机会吗? @Jamie 我的答案中的沙箱链接是 Typescript 版本。你在看什么类型的?或者你的问题到底是什么?如果您有其他问题,请随时提出新问题,您可以在此处发表评论中的链接。 我分叉了你的沙箱并添加了类型-你可以查看here,你也可以在下面看到我的答案。【参考方案2】:

我想扩展 Ajeet 的答案,以便那些希望在整个 Redux 存储中实现完全类型安全的人可以访问它。

主要区别在于您需要声明RootState 类型,该类型记录在in the RTK docs

const combinedReducer = combineReducers(
  counter: counterReducer
);

export type RootState = ReturnType<typeof combinedReducer>;

然后在执行 logout 函数的 rootReducer 中,您希望通过将 state 参数赋予 RootState 类型和 action 参数 AnyAction 来一直保持类型安全.

难题的最后一部分是将您的状态设置为RootState 类型的空对象,而不是undefined

const rootReducer: Reducer = (state: RootState, action: AnyAction) => 
  if (action.type === "counter/logout") 
    state =  as RootState;
  
  return combinedReducer(state, action);
;

我在CodeSandbox上fork了Ajeet的答案,添加了需要的类型,大家可以查看here。

【讨论】:

【参考方案3】:

带有两个 reducer 的简化示例:

// actions and reducer for state.first
const resetFirst = () => ( type: 'FIRST/RESET' );

const firstReducer = (state = initialState, action) => 
    switch (action.type) 
        // other action types here

        case 'FIRST/RESET':
            return initialState;

        default:
            return state;
    
;


// actions and reducer for state.second
const resetSecond = () => ( type: 'SECOND/RESET' );

const secondReducer = (state = initialState, action) => 
    switch (action.type) 
        // other action types here

        case 'SECOND/RESET':
            return initialState;

        default:
            return state;
    
;


const rootReducer = combineReducers(
    first: firstReducer,
    second: secondReducer
);

// thunk action to do global logout
const logout = () => (dispatch) => 
    // do other logout stuff here, for example logging out user with backend, etc..

    dispatch(resetFirst());
    dispatch(resetSecond());
    // Let every one of your reducers reset here.
;

【讨论】:

【参考方案4】:

如果您希望将每个切片重置为其初始状态(与将整个状态设置为空对象不同),您可以使用 extraReducers 响应 logout 操作并返回初始状态。

在 auth.tsx 中:

const logout = createAction('auth/logout')

在 foo.tsx 中:

const initialState = 
  bar: false,


const fooSlice = createSlice(
  name: 'foo',
  initialState,
  reducers: ,
  extraReducers: (builder) => 
    builder.addCase(logout, () => 
      return initialState
    )
  ,
)

【讨论】:

这是我的首选方法

以上是关于使用 @reduxjs/toolkit 中的 configureStore 时如何重置 Redux Store 的状态?的主要内容,如果未能解决你的问题,请参考以下文章

createAsyncThunk 和使用 redux-toolkit 编写 reducer 登录

Redux Toolkit RTK Query 发送查询参数

Redux Toolkit linting 悖论

更新切片中的状态,将对象作为有效负载 [重复]

如何为 createSlice() 的 extraReducers 编写测试(Jest + Enzyme)?

无法从 redux 存储中删除对象