77

I need to reset current state to initial state. But all my attempts were unsuccessful. How can I do it using redux-toolkit?

const showOnReviewSlice = createSlice({
  name: 'showOnReview',
  initialState: {
    returned: [],
  },
  reducers: {
    reset(state) {
      //here I need to reset state of current slice
    },
  },
});

Ruli
  • 2,592
  • 12
  • 30
  • 40
Zireael
  • 875
  • 1
  • 6
  • 5

8 Answers8

136

Something like this:

const intialState = {
  returned: []
}

const showOnReviewSlice = createSlice({
    name: 'showOnReview',
    initialState,
    reducers: {
        reset: () => initialState
    }
});
i9or
  • 1,998
  • 1
  • 14
  • 20
  • 7
    yes, it works. Also correct is reset() {return initialState} or reset: () => initialState – Zireael Dec 25 '19 at 09:06
  • 16
    I thought just setting `state = initialState` would suffice because in other examples, we only need to mutate `state`. Alas, resetting the state needed a different approach :/ – Dorklord Jun 09 '20 at 08:38
  • 6
    I must admin that even redux-toolkit is awesome it is littlebit immutability dark-magic :D Btw `return { ...initialState }` also works... – Baterka Dec 10 '20 at 23:36
  • Thanks for the tip. Returning the value did it. I wasted some time by assigning initialState to the state by assuming that immer would do the magic underneath! – Ravi Jan 09 '21 at 11:50
  • Year later got the notification, which made me curious if my answer became obsolete :) I have prepared a small demo which clearly shows that provided answer is still perfectly valid and works with the latest redux-toolkit, please check it here: https://codesandbox.io/s/initial-state-redux-toolkit-12d1-9lpu7?file=/src/features/counterSlice.js – i9or Mar 08 '21 at 22:19
  • @dorklord yeah, *why* isn't `state = initialstate` sufficient? – nachtigall Sep 22 '21 at 12:02
  • Also the `state => initialState `doesn't need the `state` in it. As the second answer shows, `() => initialState` suffices – fullStackChris Nov 24 '21 at 23:16
  • 5
    @Dorklord `state = initialState` doesn't work because you are changing the reference and redux toolkit has no way of knowing that as it keeps the old reference. You must **either** mutate the original `state` **OR** *return* new object. @nachtigall @fullStackChris – Qwerty May 05 '22 at 23:35
  • 2
    @Baterka `return { ...initialState }` is unnecessary and perhaps you come from traditional redux where mutation was forbidden. Here `return initialState` and `return { ...initialState }` are equivalent. – Qwerty May 05 '22 at 23:37
17

This worked for me (mid-late 2020). Formatted with your code context as an example.

const initialState = {
  returned: [],
};

const showOnReviewSlice = createSlice({
  name: 'showOnReview',
  initialState,
  reducers: {
    reset: () => initialState,
  },
});

Ansjovis86
  • 1,506
  • 5
  • 17
  • 48
worstestes
  • 186
  • 1
  • 3
15

When using multiple slices, all slices can be reverted to their initial state using extraReducers.

First, create an action that can be used by all slices:

export const revertAll = createAction('REVERT_ALL')

In every slice add an initialState, and an extraReducers reducer using the revertAll action:

const initialState = {};
export const someSlice = createSlice({
  name: 'something',
  initialState,
  extraReducers: (builder) => builder.addCase(revertAll, () => initialState),
  reducers: {}
});

The store can be created as usual:

export const store = configureStore({
  reducer: {
    someReducer: someSlice.reducer,
  }
})

And in your react code you can call the revertAll action with the useDispatch hook:

export function SomeComponent() {
  const dispatch = useDispatch();

  return <span onClick={() => dispatch(revertAll())}>Reset</span>
}
Blee
  • 419
  • 5
  • 11
13

Replacing state with initialState directly did not work for me (mid 2020). What I finally got working was to copy each property over with Object.assign(). This worked:

const showOnReviewSlice = createSlice({
    name: 'showOnReview',
    initialState: {
        returned: []
    },
    reducers: {
        reset(state) {
            Object.assign(state, initialState)
        }
    }
});
Freedom_Ben
  • 11,247
  • 10
  • 69
  • 89
7

We do it like this guys.

Suppose you want to clear all the data at the point of logging out.

In your store.tsx file:

import { AnyAction, combineReducers, configureStore } from '@reduxjs/toolkit';
import authReducer from './slices/authSlice'
import messageReducer from './slices/messageSlice'



const appReducer = combineReducers({
  auth: authReducer,
  message: messageReducer,
});

const reducerProxy = (state: any, action: AnyAction) => {
  if(action.type === 'logout/LOGOUT') {
    return appReducer(undefined, action);
  }
  return appReducer(state, action);
}

export const store = configureStore({
  reducer: reducerProxy,
});

Then you create a thunk like this:

export const logout = createAsyncThunk(
    "auth/logout",
    async function (_payload, thunkAPI) {
        thunkAPI.dispatch({ type: 'logout/LOGOUT' });
        console.log('logged out')
    }
);

After you've dispatched the logout thunk, you can also refresh the browser so it auto-redirects the user to the login page.

function MyReactComponent() {
   const dispatch = useDispatch()

   function myLogoutOnClickHandler() {
      dispatch(logout())
      window.location = window.location;
   }
   ...

}
Gilbert
  • 2,699
  • 28
  • 29
6

In my case, as the previous answer, mid 2021, just setting the initial state DO NOT WORK, even if you use the toolkit adapter like :

reducers: {
        // Other reducers
         state = tasksAdapter.getInitialState({
                status: 'idle',
                error: null,
                current: null
            })
        }
    },


instead, you should use Object.assign(), guess that it's related with the internal immer library behavior

Frank Simon
  • 91
  • 1
  • 4
3

Try this. In my case, I wanted to return all slices to initialState when a certain action is dispatched.

  • First, let's create action:
import { createAction } from '@reduxjs/toolkit';

export const resetPanelsAction = createAction('resetPanelsData');
  • When creating our store, we save a copy of the initialState in the middleware:
import { Middleware } from '@reduxjs/toolkit';

export const resetDataMiddleware: Middleware =
    ({ getState }) =>
    (next) => {
        // "Caching" our initial app state
        const initialAppState = getState();

        return (action) => {
            // Let's add the condition that if the action is of 
            // type resetData, then add our cached state to its payload
            if (action.type === 'resetData') {
                const actionWithInitialAppState = {
                    ...action,
                    payload: initialAppState,
                };

                return next(actionWithInitialAppState);
            }

            return next(action);
        };
    };
  • Almost done! Now let's change our root reducer a little by adding a wrapper that will check the action type, and if it is equal to resetData, then return combinedReducers with our initialState, which will be in payload.
import { AnyAction } from 'redux';
import { combineReducers } from '@reduxjs/toolkit';

export const combinedReducers = combineReducers({
    /** Your reducers */
});

export const rootReducer = (
    state: ReturnType<typeof combinedReducers> | undefined,
    action: AnyAction,
) => {
    if (action.type === 'resetPanelsData') {
        return combinedReducers(action.payload, action);
    }

    return combinedReducers(state, action);
};
jjswift
  • 31
  • 3
1

You can use spread opearator for initialState

const initialState: {
  returned: unknown[] //set your type here
} = {
  returned: []
}

const showOnReviewSlice = createSlice({
  name: 'showOnReview',
  initialState,
  reducers: {
    reset() {
      return {
        ...initialState
      }
    }
  }
});
Andrey
  • 33
  • 5