score:4

Accepted answer

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

score:7

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

score:13

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.

score:37

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


Related Query

More Query from same tag