Learnitweb

Redux Toolkit example

1. Introduction

In this tutorial we’ll rewrite our Redux example we created earlier with Redux Toolkit.

2. Configure Store with Redux Toolkit

The configureStore API from Redux Toolkit creates a Redux store that holds the complete state tree of your app, and also automatically configure the Redux DevTools extension so that you can inspect the store. The configureStore API takes 3 arguments:

  • reducer: A root reducer function that returns the next state tree, given the current state tree and an action to handle.
  • preloadedState: The initial state. This is optional.
  • enhancer: The store enhancer. This is optional.

Here is our store.js without Redux Toolkit.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { legacy_createStore as createStore, applyMiddleware } from "redux";
import reducer from "./reducer";
const store = createStore(reducer);
export default store;
import { legacy_createStore as createStore, applyMiddleware } from "redux"; import reducer from "./reducer"; const store = createStore(reducer); export default store;
import { legacy_createStore as createStore, applyMiddleware } from "redux";

import reducer from "./reducer";

const store = createStore(reducer);
export default store;

The store.js can be rewritten with Redux Toolkit as.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { configureStore } from "@reduxjs/toolkit";
import reducer from "./reducer";
const store = configureStore({ reducer: reducer });
export default store;
import { configureStore } from "@reduxjs/toolkit"; import reducer from "./reducer"; const store = configureStore({ reducer: reducer }); export default store;
import { configureStore } from "@reduxjs/toolkit";
import reducer from "./reducer";

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

3. Defining actions with Redux Toolkit

The createAction is a helper function for defining a Redux action type and creator. Typically, in Redux, you define an action by separately declaring a constant for the action type and an action creator function that constructs actions of that type. The createAction helper streamlines the process by merging these two declarations into a single step. It accepts an action type and returns an action creator for that type. This action creator can be invoked with or without arguments, attaching a payload to the action if provided.

Here is our code without Redux Toolkit.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import * as actionTypes from "./actionTypes";
export const addTask = (task) => {
return { type: actionTypes.ADD_TASK, payload: { task: task } };
};
export const removeTask = (id) => {
return { type: actionTypes.REMOVE_TASK, payload: { id: id } };
};
export const completedTask = (id) => {
return { type: actionTypes.TASK_COMPLETED, payload: { id: id } };
};
import * as actionTypes from "./actionTypes"; export const addTask = (task) => { return { type: actionTypes.ADD_TASK, payload: { task: task } }; }; export const removeTask = (id) => { return { type: actionTypes.REMOVE_TASK, payload: { id: id } }; }; export const completedTask = (id) => { return { type: actionTypes.TASK_COMPLETED, payload: { id: id } }; };
import * as actionTypes from "./actionTypes";
export const addTask = (task) => {
  return { type: actionTypes.ADD_TASK, payload: { task: task } };
};
export const removeTask = (id) => {
  return { type: actionTypes.REMOVE_TASK, payload: { id: id } };
};
export const completedTask = (id) => {
  return { type: actionTypes.TASK_COMPLETED, payload: { id: id } };
};

Here is the code with Redux Toolkit.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { createAction } from "@reduxjs/toolkit";
export const addTask = createAction("ADD_TASK");
export const removeTask = createAction("REMOVE_TASK");
export const taskCompleted = createAction("TASK_COMPLETED");
import { createAction } from "@reduxjs/toolkit"; export const addTask = createAction("ADD_TASK"); export const removeTask = createAction("REMOVE_TASK"); export const taskCompleted = createAction("TASK_COMPLETED");
import { createAction } from "@reduxjs/toolkit";

export const addTask = createAction("ADD_TASK");
export const removeTask = createAction("REMOVE_TASK");
export const taskCompleted = createAction("TASK_COMPLETED");

You need to change the reducers as well. If our reducer, we had switch case:

Here, is the code without Redux Toolkit:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
switch(action.type) {
case actionTypes.ADD_TASK:
.............
case actionTypes.REMOVE_TASK:
.............
case actionTypes.TASK_COMPLETED:
.............
}
switch(action.type) { case actionTypes.ADD_TASK: ............. case actionTypes.REMOVE_TASK: ............. case actionTypes.TASK_COMPLETED: ............. }
switch(action.type) {
	case actionTypes.ADD_TASK:
		.............
	case actionTypes.REMOVE_TASK:
		.............
	case actionTypes.TASK_COMPLETED:
		.............
}

This can be written with Redux Toolkit as:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
switch(action.type) {
case addTask.type`:
.............
case removeTask.type:
.............
case taskCompleted.type:
.............
}
switch(action.type) { case addTask.type`: ............. case removeTask.type: ............. case taskCompleted.type: ............. }
switch(action.type) {
	case addTask.type`:
		 .............
	case removeTask.type:
		 .............
	case taskCompleted.type:
		 .............
}

In the index.js, you need to pass the payload.

Here is the code snippet from index.js.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
store.dispatch(addTask("Task 1"));
store.dispatch(removeTask(1);
store.dispatch(addTask("Task 1")); store.dispatch(removeTask(1);
store.dispatch(addTask("Task 1"));
store.dispatch(removeTask(1);

Here is the same code with Redux Toolkit.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
store.dispatch(addTask({ task: "Task 1" }));
store.dispatch(removeTask({ id: 2 }));
store.dispatch(addTask({ task: "Task 1" })); store.dispatch(removeTask({ id: 2 }));
store.dispatch(addTask({ task: "Task 1" }));
store.dispatch(removeTask({ id: 2 }));

4. Creating Reducer with Toolkit

The createReducer() is a utility that simplifies creating Redux reducer functions. Internally, it employs Immer to significantly simplify immutable update logic, allowing you to write “mutative” code within your reducers. Additionally, it supports the direct mapping of specific action types to case reducer functions, which will update the state when the corresponding action is dispatched.
Here is the code of reducer.js without using Redux Toolkit:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import * as actionTypes from "./actionTypes";
let id = 0;
export default function reducer(state = [], action) {
switch (action.type) {
case actionTypes.ADD_TASK:
return [
...state,
{
id: ++id,
task: action.payload.task,
completed: false,
},
];
case actionTypes.REMOVE_TASK:
return state.filter((task) => task.id != action.payload.id);
case actionTypes.TASK_COMPLETED:
return state.map((task) =>
task.id === action.payload.id
? {
...task,
completed: true,
}
: task
);
default:
return state;
}
}
import * as actionTypes from "./actionTypes"; let id = 0; export default function reducer(state = [], action) { switch (action.type) { case actionTypes.ADD_TASK: return [ ...state, { id: ++id, task: action.payload.task, completed: false, }, ]; case actionTypes.REMOVE_TASK: return state.filter((task) => task.id != action.payload.id); case actionTypes.TASK_COMPLETED: return state.map((task) => task.id === action.payload.id ? { ...task, completed: true, } : task ); default: return state; } }
import * as actionTypes from "./actionTypes";
let id = 0;
export default function reducer(state = [], action) {
  switch (action.type) {
    case actionTypes.ADD_TASK:
      return [
        ...state,
        {
          id: ++id,
          task: action.payload.task,
          completed: false,
        },
      ];
    case actionTypes.REMOVE_TASK:
      return state.filter((task) => task.id != action.payload.id);
    case actionTypes.TASK_COMPLETED:
      return state.map((task) =>
        task.id === action.payload.id
          ? {
              ...task,
              completed: true,
            }
          : task
      );
    default:
      return state;
  }
}

Here is the code of reducer.js with Redux Toolkit.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { addTask, removeTask, taskCompleted } from "./action";
import { createReducer } from "@reduxjs/toolkit";
let id = 0;
export default createReducer([], (builder) => {
builder.addCase(addTask, (state, action) => {
state.push({
id: ++id,
task: action.payload.task,
completed: false,
});
}),
builder.addCase(removeTask, (state, action) => {
const index = state.findIndex((task) => task.id == action.payload.id);
state.splice(index, 1);
}),
builder.addCase(taskCompleted, (state, action) => {
const index = state.findIndex((task) => task.id == action.payload.id);
state[index].completed = true;
});
});
import { addTask, removeTask, taskCompleted } from "./action"; import { createReducer } from "@reduxjs/toolkit"; let id = 0; export default createReducer([], (builder) => { builder.addCase(addTask, (state, action) => { state.push({ id: ++id, task: action.payload.task, completed: false, }); }), builder.addCase(removeTask, (state, action) => { const index = state.findIndex((task) => task.id == action.payload.id); state.splice(index, 1); }), builder.addCase(taskCompleted, (state, action) => { const index = state.findIndex((task) => task.id == action.payload.id); state[index].completed = true; }); });
import { addTask, removeTask, taskCompleted } from "./action";
import { createReducer } from "@reduxjs/toolkit";
let id = 0;

export default createReducer([], (builder) => {
  builder.addCase(addTask, (state, action) => {
    state.push({
      id: ++id,
      task: action.payload.task,
      completed: false,
    });
  }),
    builder.addCase(removeTask, (state, action) => {
      const index = state.findIndex((task) => task.id == action.payload.id);
      state.splice(index, 1);
    }),
    builder.addCase(taskCompleted, (state, action) => {
      const index = state.findIndex((task) => task.id == action.payload.id);
      state[index].completed = true;
    });
});

5. Combine action and reducer with createSlice

A function that accepts an initial state, an object of reducer functions, and a “slice name”, and automatically generates action creators and action types that correspond to the reducers and state. Internally, it uses createAction and createReducer, so you may also use Immer to write “mutating” immutable updates.

Here is the code of taskSlice.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { createSlice } from "@reduxjs/toolkit";
let id = 0;
const taskSlice = createSlice({
name: "tasks",
initialState: [],
reducers: {
addTask: (state, action) => {
state.push({
id: ++id,
task: action.payload.task,
completed: false,
});
},
removeTask: (state, action) => {
const index = state.findIndex((task) => task.id == action.payload.id);
state.splice(index, 1);
},
taskCompleted: (state, action) => {
const index = state.findIndex((task) => task.id == action.payload.id);
state[index].completed = true;
},
},
});
export const { addTask, removeTask, taskCompleted } = taskSlice.actions;
export default taskSlice.reducer;
import { createSlice } from "@reduxjs/toolkit"; let id = 0; const taskSlice = createSlice({ name: "tasks", initialState: [], reducers: { addTask: (state, action) => { state.push({ id: ++id, task: action.payload.task, completed: false, }); }, removeTask: (state, action) => { const index = state.findIndex((task) => task.id == action.payload.id); state.splice(index, 1); }, taskCompleted: (state, action) => { const index = state.findIndex((task) => task.id == action.payload.id); state[index].completed = true; }, }, }); export const { addTask, removeTask, taskCompleted } = taskSlice.actions; export default taskSlice.reducer;
import { createSlice } from "@reduxjs/toolkit";
let id = 0;

const taskSlice = createSlice({
  name: "tasks",
  initialState: [],
  reducers: {
    addTask: (state, action) => {
      state.push({
        id: ++id,
        task: action.payload.task,
        completed: false,
      });
    },
    removeTask: (state, action) => {
      const index = state.findIndex((task) => task.id == action.payload.id);
      state.splice(index, 1);
    },
    taskCompleted: (state, action) => {
      const index = state.findIndex((task) => task.id == action.payload.id);
      state[index].completed = true;
    },
  },
});

export const { addTask, removeTask, taskCompleted } = taskSlice.actions;
export default taskSlice.reducer;

6. Combining reducers with redux-toolkit

Suppose there are two reducers: employeeReducer and taskReducer. Our store can be configured to use these two reducers.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { configureStore } from "@reduxjs/toolkit";
import employeeReducer from "./employees";
import taskReducer from "./tasks";
const store = configureStore({
reducer: {
tasks: taskReducer,
employees: employeeReducer,
},
});
export default store;
import { configureStore } from "@reduxjs/toolkit"; import employeeReducer from "./employees"; import taskReducer from "./tasks"; const store = configureStore({ reducer: { tasks: taskReducer, employees: employeeReducer, }, }); export default store;
import { configureStore } from "@reduxjs/toolkit";
import employeeReducer from "./employees";
import taskReducer from "./tasks";

const store = configureStore({
  reducer: {
    tasks: taskReducer,
    employees: employeeReducer,
  },
});
export default store;

7. Conclusion

In this tutorial, we updated our earlier example of Redux with Redux Toolkit.