Published: February 17 2023

Redux Toolkit - Fix "The object notation for `createSlice.extraReducers` is deprecated" in React

Tutorial built with React 18.2, Redux 4.2 and Redux Toolkit 1.9.2

I noticed the following warning in the browser console yesterday while working on a React app that uses the Redux Toolkit:

The object notation for `createSlice.extraReducers` is deprecated, and will be removed in RTK 2.0.
Please use the 'builder callback' notation instead: https://redux-toolkit.js.org/api/createSlice


Cause of the RTK deprecation warning

At first I wasn't sure exactly what I needed to do, but after reading the documentation the warning made more sense. I was passing my extraReducers to the Redux Toolkit createSlice() function as a plain JavaScript object (using object notation) instead of a function that accepts a builder parameter (the builder callback).

Extra Reducers with object notation

This is the extra reducers function from my Redux 'auth' slice before fixing the warning, it returns the reducers using object notation:

function createExtraReducers() {
    return {
        ...login()
    };

    function login() {
        var { pending, fulfilled, rejected } = extraActions.login;
        return {
            [pending]: (state) => {
                state.error = null;
            },
            [fulfilled]: (state, action) => {
                const user = action.payload;
                
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem('user', JSON.stringify(user));
                state.user = user;

                // get return url from location state or default to home page
                const { from } = history.location.state || { from: { pathname: '/' } };
                history.navigate(from);
            },
            [rejected]: (state, action) => {
                state.error = action.error;
            }
        };
    }
}


How to fix the warning

The fix was pretty simple, update the return type to a function with a builder parameter, and replace the key: value pairs of the returned JavaScript object with calls to builder.addCase(key, value).

Extra Reducers with builder callback

This is the fixed extra updated function from my Redux 'auth' slice that returns a builder callback function:

function createExtraReducers() {
    return (builder) => {
        login();

        function login() {
            var { pending, fulfilled, rejected } = extraActions.login;
            builder
                .addCase(pending, (state) => {
                    state.error = null;
                })
                .addCase(fulfilled, (state, action) => {
                    const user = action.payload;

                    // store user details and basic auth data in local storage to keep user logged in between page refreshes
                    localStorage.setItem('user', JSON.stringify(user));
                    state.user = user;

                    // get return url from location state or default to home page
                    const { from } = history.location.state || { from: { pathname: '/' } };
                    history.navigate(from);
                })
                .addCase(rejected, (state, action) => {
                    state.error = action.error;
                });
        }
    };
}


Complete Redux Auth Slice

Path: /src/_store/auth.slice.js

Below is the complete Redux auth slice that includes the above createExtraReducers() function, it's from a React login tutorial I posted recently that's available at React 18 + Redux - Basic HTTP Authentication Example & Tutorial.

The auth slice manages Redux state, actions and reducers for authentication. The file is organised into three sections to make it easier to see what's going on. The first section calls functions to create and configure the slice, the second section exports the actions and reducer, and the third section contains the functions that implement the logic.

initialState defines the state properties in the slice with their initial values. The user state property holds the current logged in user, it is initialized with the 'user' object from local storage to support staying logged in between page refreshes and browser sessions, or null if localStorage is empty. The error is displayed in the login component if login failed.

The reducers object passed to createSlice() contains logic for synchronous actions (things you don't have to wait for). For example the logout reducer sets the user state property to null, removes it from local storage and redirects to the login page. It doesn't perform any async tasks such as an API request. The createSlice() function auto generates matching actions for these reducers and exposes them via the slice.actions property.

Async Actions with createAsyncThunk()

The extraActions object contains logic for asynchronous actions (things you have to wait for) such as API requests. Async actions are created with the Redux Toolkit createAsyncThunk() function. The extraReducers object contains methods for updating Redux state at different stages of async actions (pending, fulfilled, rejected), and is passed as a parameter to the createSlice() function.

The login() action method posts credentials to the API, on success (fulfilled) the returned user object is stored in the Redux state user prop and localStorage, and the user is redirected to the return url or home page. On fail (rejected) the error is stored in the Redux state error property which is displayed inside the login component.

Export Actions and Reducer for Redux Slice

The authActions export includes all sync actions (slice.actions) and async actions (extraActions) for the auth slice.

The reducer for the auth slice is exported as authReducer, which is used by the app to configure the global Redux state store.

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

import { history, fetchWrapper } from '_helpers';

// create slice

const name = 'auth';
const initialState = createInitialState();
const reducers = createReducers();
const extraActions = createExtraActions();
const extraReducers = createExtraReducers();
const slice = createSlice({ name, initialState, reducers, extraReducers });

// exports

export const authActions = { ...slice.actions, ...extraActions };
export const authReducer = slice.reducer;

// implementation

function createInitialState() {
    return {
        // initialize state from local storage to enable user to stay logged in
        user: JSON.parse(localStorage.getItem('user')),
        error: null
    }
}

function createReducers() {
    return {
        logout
    };

    function logout(state) {
        state.user = null;
        localStorage.removeItem('user');
        history.navigate('/login');
    }
}

function createExtraActions() {
    const baseUrl = `${process.env.REACT_APP_API_URL}/users`;

    return {
        login: login()
    };

    function login() {
        return createAsyncThunk(
            `${name}/login`,
            async ({ username, password }) => await fetchWrapper.post(`${baseUrl}/authenticate`, { username, password })
        );
    }
}

function createExtraReducers() {
    return (builder) => {
        login();

        function login() {
            var { pending, fulfilled, rejected } = extraActions.login;
            builder
                .addCase(pending, (state) => {
                    state.error = null;
                })
                .addCase(fulfilled, (state, action) => {
                    const user = action.payload;

                    // store user details and basic auth data in local storage to keep user logged in between page refreshes
                    localStorage.setItem('user', JSON.stringify(user));
                    state.user = user;

                    // get return url from location state or default to home page
                    const { from } = history.location.state || { from: { pathname: '/' } };
                    history.navigate(from);
                })
                .addCase(rejected, (state, action) => {
                    state.error = action.error;
                });
        }
    };
}


More info on the Redux Toolkit createSlice function is available at https://redux-toolkit.js.org/api/createSlice

 


Need Some React Help?

Search fiverr for freelance React developers.


Follow me for updates

On Twitter or RSS.


When I'm not coding...

Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!


Comments


Supported by