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
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
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!