In a previous lesson, you learned about createSlice. In this lesson, you will learn about extraReducers, a property you can optionally pass to createSlice that allows createSlice to respond to action types it did not generate.

To refresh your memory, createSlice accepts a single argument, options, which is an object containing configuration parameters including a name, some initial state, and reducers. createSlice then uses these configuration parameters to generate a slice of the store, including action creators and action types for updating the state contained in that slice. Consider the following example:

const usersSlice = createSlice({ name: 'users', initialState: { users: [] }, reducers: { addUser: (state, action) => { state.users.push(action.payload) } }, })

This call to createSlice, generates a slice of the store that responds to the action creator usersSlice.actions.addUser. But what if we’ve generated our action creators via calls to createAsyncThunk? Consider fetchUserById, the asynchronous action creator from earlier in this lesson:

const fetchUserById = createAsyncThunk( 'users/fetchUserById', // action type async (userId, thunkAPI) => { // payload creator const response = await fetchUser(userId) return response.data } )

This asynchronous action creator will generate three action types: 'users/fetchUserById/pending', 'users/fetchUserById/fulfilled', and 'users/fetchUserById/rejected'. Currently, these action types have no effect on our users slice, which only responds to the users/addUser action type generated by createSlice.

How can we account for these promise lifecycle action types in our user slice? This is exactly the problem that extraReducers, an optional property on the configuration object passed to createSlice, was designed to solve. extraReducers allows createSlice to respond to action types generated elsewhere. To make the users slice respond to promise lifecycle action types, we pass them to createSlice in the extraReducers property.

Open usersSlice.js in your code editor to see an example of the extraReducers property in context.

Note that in addition to using the extraReducers property, we also added some extra fields to our state object: a boolean, isLoading, which will be true when a request is pending, and otherwise false, and a boolean hasError, which we will set to true if our request to fetch a user is rejected. These additions allow us to track promise lifecycle states so that we can create satisfying and informative user interfaces when the promise is either pending or rejected. When the promise is fulfilled these are set to false and the user data is added to the state.



In allRecipesSlice.js, we’ve used createAsyncThunk to define loadRecipes, an asynchronous action creator that fetches all our app’s recipes, and createSlice to define a slice of recipes in our app’s store.

Add two booleans — isLoading and hasError — to the initialState property passed to createSlice. What should their initial values be?


Using the extraReducers property, add reducers for each of the promise lifecycle action types generated by createAsyncThunk.

What about the app’s behavior has changed? While the recipes are being fetched, the app displays a loading spinner. And if the recipes fail to fetch, the app displays an error message.

Why does the app behave differently when you pass extra Reducers to createSlice? Adding the extra reducers to the recipes slice causes the store to update in response to each of the pending/fulfilled/rejected actions dispatched by loadRecipes. These changes are reflected in the app’s UI.

Take this course for free

Mini Info Outline Icon
By signing up for Codecademy, you agree to Codecademy's Terms of Service & Privacy Policy.

Or sign up using:

Already have an account?