
Chapter Outline
State Management with Redux and Redux Toolkit
In the realm of React development, managing state effectively is crucial for building responsive and maintainable applications. Redux has long been a cornerstone of the React ecosystem, providing a predictable state container for JavaScript apps. The Redux Toolkit, introduced more recently, offers a more efficient, powerful way to write Redux logic. This article, part of the "Modern React.js Series" under "State Management in React," explores how to leverage Redux and Redux Toolkit to streamline your state management practices.
Introduction to Redux
Redux is a state management library that helps you manage the state of your application in a single global object, which Redux refers to as the "store." Redux is most commonly used with React, but it can be used with any other JavaScript framework or library.
Core Concepts of Redux
Redux revolves around three fundamental principles:
1. Single Source of Truth
The state of your entire application is stored in an object tree within a single store. This makes it easier to debug or inspect an application at any point in time.
2. State is Read-only
The only way to change the state is to emit an action, an object describing what happened. This ensures that neither the views nor the network callbacks will ever write directly to the state.
3. Changes are Made with Pure Functions
To specify how the state tree is transformed by actions, you write pure reducers. Reducers are pure functions that take the previous state and an action, and return the next state.
Implementing Redux in a React Application
To demonstrate how Redux works within a React application, let's walk through a simple example where we will create a counter that increments or decrements based on user interaction.
Step 1: Setting Up
Let us first create a React application. We will use Vite to setup the scaffold of a React application using JavaScript.
Follow the instructions on the Getting Started to setup the project. With that out of the way. we'll install Redux and React-Redux:
bashnpm install redux react-redux
Step 2: Create Redux Store
Create a file named store.js:
jsx1import { createStore } from 'redux';23// Reducer function4const counterReducer = (state = { count: 0 }, action) => {5 switch (action.type) {6 case 'INCREMENT':7 return { ...state, count: state.count + 1 };8 case 'DECREMENT':9 return { ...state, count: state.count - 1 };10 default:11 return state;12 }13};1415// Create Redux store16const store = createStore(counterReducer);1718export default store;
This reducer handles two actions: incrementing and decrementing the count.
Step 3: Provide Store to React
In your root component file (usually main.jsx or App.jsx), use the Provider from react-redux to pass the Redux store to your React components:
jsx1import React from 'react';2import ReactDOM from 'react-dom';3import { Provider } from 'react-redux';4import App from './App';5import store from './store';67ReactDOM.render(8 <Provider store={store}>9 <App />10 </Provider>,11);
Step 4: Connecting React Components
Now, let's create a Counter component that connects to the Redux store:
jsx1import React from "react";2import { useSelector, useDispatch } from "react-redux";34function Counter() {5 const count = useSelector((state) => state.count); // Access state from the store6 const dispatch = useDispatch(); // To dispatch actions78 return (9 <div>10 <h1>{count}</h1>11 <button12 onClick={() => dispatch({ type: "INCREMENT" })}13 style={{ margin: "1rem" }}14 >15 Increment16 </button>17 <button18 onClick={() => dispatch({ type: "DECREMENT" })}19 style={{ margin: "1rem" }}20 >21 Decrement22 </button>23 </div>24 );25}2627export default Counter;
This component uses useSelector to read from the state and useDispatch to dispatch actions.
Step 5: Use the component in the Application
Update the App.jsx to use the component:
jsx1function App() {2 return (3 <>4 // ...5 <div className="card">6 <Counter />7 <p>8 Edit <code>src/App.jsx</code> and save to test HMR9 </p>10 </div>11 // ...12 </>13 )14}
Introduction to Redux Toolkit
Redux Toolkit is the official, opinionated, batteries-included toolset for efficient Redux development. It is intended to be the standard way to write Redux logic. It was created to address the complexities and repetitive patterns that Redux can often entail, and to provide a simplified API for working with Redux.
Why Use Redux Toolkit?
Redux Toolkit simplifies Redux application development and incorporates best practices automatically. Here are some of its benefits:
- Simplification: Provides utilities that simplify common use cases like store setup, creating reducers, immutable update logic, and more.
- Performance: Automatically uses the
immerlibrary to allow you to write simpler immutable update logic by mutating state in a "draft state". - Best Practices: Encourages good Redux architecture and offers features like built-in Thunk support for async logic and other useful addons.
Integrating Redux Toolkit in a React Application
Let's upgrade the basic Redux example from the previous section by incorporating Redux Toolkit. We will update our increment/decrement counter application built using React and Vite.
Step 1: Install Redux Toolkit and React-Redux
First, install Redux Toolkit and React-Redux if they are not already included:
bashnpm install @reduxjs/toolkit react-redux
Step 2: Create Redux Store with Redux Toolkit
Use Redux Toolkit's configureStore() to create the Redux store. This function automatically sets up the Redux DevTools extension and thunk middleware.
src/app/store.js1import { configureStore } from '@reduxjs/toolkit';23export const store = configureStore({4 reducer: {5 // Reducers will go here6 },7});
Step 3: Define Slice using createSlice()
Redux Toolkit utilizes the concept of "slices" of state, which typically correspond to a piece of the state and the reducer logic needed to manage it. The createSlice() function automatically generates action creators and action types that correspond to the reducers and state.
src/reducers/incdec/incdecSlice.js1import { createSlice } from "@reduxjs/toolkit";23export const incdecSlice = createSlice({4 name: "incdec",5 initialState: {6 count: 0,7 },8 reducers: {9 increment: (state, action) => {10 state.count += 1;11 },12 decrement: (state, action) => {13 state.count -= 1;14 },15 },16});1718export const { increment, decrement } = incdecSlice.actions;19export default incdecSlice.reducer;
Step 4: Add the Reducer to the Store
Now integrate the todo slice reducer into the store.
src/app/store.js1import { configureStore } from '@reduxjs/toolkit';2import incdecReducer from './reducers/incdec/incdecSlice';34export const store = configureStore({5 reducer: {6 incdec: incdecReducer,7 },8});
Step 5: Set Up the Provider in the App
Wrap your application in the Provider component from react-redux and pass in the store.
src/main.jsx1import React from 'react';2import ReactDOM from 'react-dom';3import App from './App';4import { Provider } from 'react-redux';5import { store } from './app/store';67ReactDOM.createRoot(document.getElementById('root')).render(8 <React.StrictMode>9 <Provider store={store}>10 <App />11 </Provider>12 </React.StrictMode>13);
Step 6: Use Redux State and Actions in React Components
Now, you can use Redux state and dispatch actions in your React components using hooks provided by react-redux.
src/components/Counter.jsx1import React, { useState } from 'react';2import { useSelector, useDispatch } from 'react-redux';3import { increment, decrement } from "../reducers/incdec/incdecSlice";45function App() {6 const count = useSelector((state) => state.incdec.count); // Access state from the store7 const dispatch = useDispatch();89 return (10 <div>11 <h1>{count}</h1>12 <button13 onClick={() => dispatch(increment())}14 style={{ margin: "1rem" }}15 >16 Increment17 </button>18 <button19 onClick={() => dispatch(decrement())}20 style={{ margin: "1rem" }}21 >22 Decrement23 </button>24 </div>25 );26}2728export default App;
In a future tutorial series I will elaborate further on developing React application using Redux and Redux ToolKit. For now, these simple demonstrations of state management should be sufficient to get a project off the ground.
Conclusion
Managing state in large React applications can be challenging, but Redux and Redux Toolkit provide powerful tools to manage state more efficiently and with less code. Whether you are maintaining a large-scale project or building a new app from scratch, integrating Redux Toolkit can help you write more readable and maintainable code. For more information and advanced use cases, check out the Redux Toolkit documentation.
By incorporating these tools into your React projects as part of the "Modern React.js Series," you ensure that your applications are scalable, maintainable, and up-to-date with the latest practices in React state management.