Codecademy Logo

Redux Toolkit

Redux Toolkit

Redux Toolkit, also known as the @reduxjs/redux-toolkit package, contains packages and functions that are essential for building a Redux app. Redux Toolkit simplifies most Redux tasks like setting up the store, creating reducers and performing immutable updates.

createSlice() Options Object

The createSlice() function is used to simplify and reduce the code needed when creating application slices. It takes an object of options as an argument. The options are:

  • name: the slice name used as the prefix of the generated action.type strings
  • initialState: the initial value for the state to be used by the reducer
  • reducers: an object containing action names as keys and their corresponding case reducers as values. The keys are used to generate action creators with the same names.
/*
The action.type strings created will be
'todos/clearTodos' and 'todos/addTodo'
*/
const options = {
name: 'todos',
initialState: [],
reducers: {
clearTodos: state => [],
addTodo: (state, action)
=> [...state, action.payload]
}
}
const todosSlice = createSlice(options);

“Mutable” Code with createSlice()

createSlice() lets you write immutable updates using “mutation-like” logic within the case reducers. This is because createSlice() uses the Immer library internally to turn mutating code into immutable updates. This helps to avoid accidentally mutating the state, which is the most commonly made mistake when using Redux.

/*
addTodo uses the mutating push() method
*/
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
clearTodos: state => [],
addTodo: (state, action)
=> state.push(action.payload)
}
});

Slices with createSlice()

createSlice() returns an object containing a slice reducer (todosSlice.reducer) and corresponding auto-generated action creators (todosSlice.actions).

  • The slice reducer is generated from the case reducers provided by options.reducers.
  • The action creators are automatically generated and named for each case reducer. The action.type values they return are a combination of the slice name ('todos') and the action name ('addTodo') separated by a forward slash (todos/addTodo).

When creating slices in separate files it is recommended to export the action creators as named exports and the reducer as a default export.

const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: (state, action)
=> state.push(action.payload)
}
});
/*
todosSlice = {
name: "todos",
reducer: (state, action) => newState,
actions: {
addTodo: (payload) => ({type: "todos/addTodo", payload})
},
caseReducers: {
addTodo: (state, action) => newState
}
}
*/
export { addTodo } = todosSlice.actions;
export default todosSlice.reducer;

Create store with configureStore()

configureStore() accepts a single configuration object as a parameter. This object should contain a reducer property that is either assigned a function to be used as the root reducer or an object of slice reducers that will be combined to create a root reducer. When the reducer property is an object, configureStore() will create a root reducer using Redux’s combineReducers() function.

import todosReducer from './todos/todosSlice';
import filterReducer from './filter/filterSlice';
const store = configureStore({
reducer: {
todos: todosReducer,
filter: filterReducer
}
});

Create the Redux Store

The createStore() helper function creates and returns a Redux store object that holds and manages the complete state tree of your app. The only required argument is a reducer function, which is called every time an action is dispatched.

The store object returned has three key methods that ensure that all interactions with the application state are executed through the store:

  • store.getState()
  • store.dispatch(action)
  • store.subscribe(listener)
const initialState = 0;
const countUpReducer = (
state = initialState,
action
) => {
switch (action.type) {
case 'increment':
return state += 1;
default:
return state;
}};
const store = createStore(countUpReducer);

The getState() Method

The getState() method of a Redux store returns the current state tree of your application. It is equal to the last value returned by the store‘s reducer.

  • In the one-way data flow model (store → view → action → store), getState is the only way for the view to access the store’s state.
  • The state value returned by getState() should not be modified directly.
const initialState = 0;
const countUpReducer = (
state = initialState,
action
) => {
switch (action.type) {
case 'increment':
return state += 1;
default:
return state;
}};
const store = createStore(countUpReducer);
console.log(store.getState());
// Output: 0

The dispatch() Method

The dispatch(action) method of a Redux store is the only way to trigger a state change. It accepts a single argument, action, which must be an object with a type property describing the change to be made. The action object may also contain additional data to pass to the reducer, conventionally stored in a property called payload.

Upon receiving the action object via dispatch(), the store’s reducer function will be called with the current value of getState() and the action object.

const initialState = 0;
const countUpReducer = (
state = initialState,
action
) => {
switch (action.type) {
case 'increment':
return state += 1;
case 'incrementBy':
return state += action.payload;
default:
return state;
}};
const store = createStore(countUpReducer);
store.dispatch({ type: 'increment' });
// state is now 1.
store.dispatch({ type: 'incrementBy'
payload: 3 });
// state is now 4.

The subscribe() Method

The subscribe(listener) method of a Redux store adds a callback function to a list of callbacks maintained by the store. When the store‘s state changes, all of the listener callbacks are executed. A function that unsubscribes the provided callback is returned from subscribe(listener).

Often, store.getState() is called inside the subscribed callback to read the current state tree.

const printCurrentState = () => {
const state = store.getState()
console.log(`state: ${state}`);
}
store.subscribe(printCurrentState);

Introduction To Redux

A React application can share multiple points of data across components. In many cases, managing the data shared can become a complex task.

Redux is a library for managing and updating application states. It provides a centralized “store” for the state that is shared across your entire application, with rules ensuring that the state can only be updated in a predictable fashion using events called “actions”.

Redux works well with applications that have a large amount of global state that is accessed by many of the application’s components. The goal of Redux is to provide scaleable and predictable state management.

Store

In Redux, a store is a container that manages the global state of your application.

As the single source of truth, the store is the center of every Redux application. It has the ability to update the global state and subscribes elements of an application’s UI to changes in the state. Rather than accessing the state directly, Redux provides functions through the store to interact with the state.

Actions

In Redux, an action is a plain JavaScript object that represents an intention to change the store’s state. Action objects must have a type property with a user-defined string value that describes the action being taken.

Optional properties can be added to the action object. One common property added is conventionally called payload, which is used to supply data necessary to perform the desired action.

/*
Basic action object for a shopping list
that removes all items from the list
*/
const clearItems = {
type: 'shopping/clear'
}
/*
Action object for a shopping list
that adds an item to the list
*/
const addItem = {
type: 'shopping/addItem',
payload: 'Chocolate Cake'
}

Reducers

In Redux, a reducer, also known as a reducing function, is a JavaScript function that takes the current state of the store and an action as parameters.

Reducers calculate the new state based on the action it receives. Reducers are the only way the store’s current state can be changed within a Redux application. They are an important part of Redux’s one-way data flow model.

/*
A reducer function that handles 2 actions
or returns the current state as a default
*/
const shoppingReducer = (
state = [],
action
) => {
switch (action.type) {
case "shopping/clear":
return [];
case "shopping/addItem":
return [
...state,
action.payload];
default:
/*
If the reducer doesn't care
about this action type, return
the existing state unchanged
*/
return state;
}
}

Installing Redux Toolkit

The @reduxjs/redux-toolkit package is added to a project by first installing it with npm.

Some of the resources imported from @reduxjs/redux-toolkit are:

  • createSlice
  • configureStore
npm install @reduxjs/redux-toolkit

Learn more on Codecademy