【问题标题】:How to reset state of Redux Store when using configureStore from @reduxjs/toolkit?使用 @reduxjs/toolkit 中的 configureStore 时如何重置 Redux Store 的状态?
【发布时间】: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吗?那真的很有帮助。
  • 见下方答案

标签: reactjs redux react-redux redux-toolkit


【解决方案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
          })
        },
      })
      

      【讨论】:

      • 这是我的首选方法
      猜你喜欢
      • 2021-04-15
      • 2020-12-14
      • 2021-02-06
      • 1970-01-01
      • 2020-04-12
      • 2021-11-17
      • 2021-09-21
      • 2019-08-10
      • 2020-10-03
      相关资源
      最近更新 更多