16

I have seen solutions for clearing/resetting the store after logout but did not understand how to implement the same functionality for the following way of setting up the redux store.

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



Here is how all the reducers implemented using createAction and createReducer from @reduxjs/toolkit.

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



4

4 回答 4

34

As per Dan Abramov's answer, create a root reducer which will simply delegate the action to your main or combined reducer. And whenever this root reducer receives a reset type of action, it resets the state.

Example:

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

So, if you have configured your store with @reduxjs/toolkit's configureStore, it might look like this:

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

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

where configureStore's first parameter reducer accepts a function (which is treated as root reducer) or an object of slice reducers which is internally converted to root reducer using combineReducers.

So, now instead of passing object of slice reducers (shown above), we can create and pass root reducer by ourselves, here is how we can do it:

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

Now, lets create a root reducer which does our reset job when needed:

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

Here is CodeSandbox

于 2020-05-21T20:51:12.767 回答
10

I wanted to extend Ajeet's answer so that it is accessible to those who want complete type safety throughout their Redux store.

The key differences are that you need to declare a RootState type, which is documented in the RTK docs

const combinedReducer = combineReducers({
  counter: counterReducer
});

export type RootState = ReturnType<typeof combinedReducer>;

And then in your rootReducer, where you are executing your logout function, you want to maintain type safety all the way down by giving the state param the RootState type, and action param AnyAction.

The final piece of the puzzle is setting your state to an empty object of type RootState instead of undefined.

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

I forked Ajeet's answer on CodeSandbox, added the required types, and you can view it here.

于 2020-10-11T12:26:46.690 回答
6

If you're looking to reset each slice to its initial state (unlike setting the entire state to an empty object) you can use extraReducers to respond to a logout action and return the initial state.

In auth.tsx:

const logout = createAction('auth/logout')

In foo.tsx:

const initialState = {
  bar: false,
}

const fooSlice = createSlice({
  name: 'foo',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(logout, () => {
      return initialState
    })
  },
})
于 2021-08-19T07:12:53.423 回答
4

A simplified example with two reducers:

// 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.
};
于 2019-12-04T08:37:22.023 回答