What is Redux? Store, Actions, and Reducers the Complete Beginner's Guide

Muhaymin Bin Mehmood

Muhaymin Bin Mehmood

· 7 min read
The Complete Front-End Developer Roadmap 2024 banner image
The Complete Front-End Developer Roadmap 2024 banner image

Redux, a popular JavaScript library, empowers developers to manage application state predictably and efficiently, particularly within React applications. This guide delves into the core concepts, benefits, and considerations of using Redux, culminating in a practical example with React and Redux Toolkit.

What is Redux?

Redux acts as a predictable state container for your JavaScript applications. It centralizes application state, ensuring all components have access to the same data source. This centralized approach simplifies state management, especially in complex applications with numerous interconnected components.

Why Use Redux?

While several state management libraries exist, Redux offers compelling advantages:

  1. Improved Organization: Centralized state leads to a cleaner codebase, fostering easier code comprehension and maintenance.
  2. Predictable Updates: Redux enforces pure functions that guarantee consistent state changes based on received actions. Makes debugging and testing less time-consuming and error-prone.
  3. Scalability: Redux effectively handles complex state management needs as your application grows in size.
  4. Testability: Pure functions and isolated reducers facilitate unit testing, ensuring code quality and stability.
  5. Debugging Tools: The Redux DevTools extension provides invaluable features like time-travel debugging and state change visualization.

Core Principles of Redux:

To grasp how Redux operates, let's explore its fundamental principles:

  1. Single Source of Truth: The entire application state resides in a centralized store, accessible by any component that needs it.
  2. Pure Functions: Reducers are pure functions that receive the current state and an action object. Based on the action type and payload, they return a new state object, never modifying the existing state. This ensures predictable and reproducible state updates.
  3. Actions: Plain JavaScript objects, they describe the intended state change. They possess the following properties:
    • type: A unique string identifying the action type.
    • payload (optional): Additional data relevant to the action, such as new item details in a todo list.

Understanding Redux Components:

  • Store: This central repository holds the application state and provides methods for:
    • Dispatching actions: Sending actions to modify the state.
    • Subscribing to state changes: Enabling components to react to state updates.
    • Getting the current state: Accessing the application's current state.
  • Actions: As described earlier, these objects describe the intended state change.
  • Reducers: Pure functions that receive the current state and an action, returning a new state based on the action type. They are the heart of Redux, responsible for state updates.

Simple React Redux Example with Redux Toolkit:

To illustrate these concepts, let's build a basic to-do list application using React and Redux Toolkit. Redux Toolkit streamlines Redux setup by offering utilities like configureStore and createSlice, simplifying common tasks.

1. Setting Up Redux Toolkit:

Install the necessary packages:

npm install react-redux redux react-redux-toolkit

2. Creating a Redux Slice:

Create a file named todoSlice.js and define the initial state, actions, and reducer using createSlice:

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  todos: [],
};

export const todoSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    addTodo: (state, action) => {
      state.todos.push({
        id: Math.random().toString(36).substring(2, 15),
        text: action.payload,
      });
    },
    removeTodo: (state, action) => {
      state.todos = state.todos.filter(
        (todo) => todo.id !== action.payload
      );
    },
  },
});

export const { addTodo, removeTodo } = todoSlice.actions;

export default todoSlice.reducer;

Explanation:

  • initialState defines the initial to-do list state as an empty array.
  • Reducers handle state updates based on action types:
    • addTodo: Pushes a new to-do object with a unique ID and text from the payload to the todos array.
    • removeTodo: Filters out the to-do with the matching ID from the todos array.

3. Creating the Redux Store:

Create a file named store.js and configure the store using configureStore:

import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './todoSlice';  // Import the reducer

export const store = configureStore({
  reducer: {
    todos: todoReducer,  // Add the reducer to the store configuration
  },
});

Explanation:

  1. Import the reducer: We import the todoReducer from ./todoSlice.js as it contains the logic for updating the state based on actions.
  2. Store configuration:
    • reducer: This property is an object where we specify the reducers that manage different parts of the application state. In this example, we only have one reducer named todos which is imported earlier.

This code configures the Redux store using configureStore from Redux Toolkit. It takes an object as an argument, where we define the reducers that manage the application state. In our case, we have a single reducer named todos imported from todoSlice.js, which is responsible for managing the to-do list state. This reducer is added to the store configuration under the key todos.

With this completed store configuration, we have a central location to manage the application state and interact with it through actions and reducers.

4. Creating React Components with Redux:

Now that we have the Redux setup with our store and reducers, let's integrate it with React components to build our to-do list application.

a. Connecting the Store:

We can wrap our entire application with the Provider component from react-redux to make the Redux store accessible to all child components:

import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store'; // Import the store
import App from './App'; // Import your main App component

const Root = () => {
  return (
    <Provider store={store}>
      <App />
    </Provider>
  );
};

export default Root;

b. Dispatching Actions:

We can use the useDispatch hook from react-redux to dispatch actions from our React components. This allows us to trigger state updates in the store:

import React from 'react';
import { useDispatch } from 'react-redux';

const AddTodoForm = () => {
  const dispatch = useDispatch();

  const handleAddTodo = (event) => {
    event.preventDefault();
    const newTodoText = event.target.elements.newTodo.value;
    dispatch(addTodo(newTodoText)); // Dispatch the addTodo action
    event.target.elements.newTodo.value = ''; // Clear the input field
  };

  return (
    <form onSubmit={handleAddTodo}>
      <input type="text" name="newTodo" placeholder="Add a new todo..." />
      <button type="submit">Add</button>
    </form>
  );
};

export default AddTodoForm;

In this example, the AddTodoForm component uses useDispatch to dispatch the addTodo action when the form is submitted, passing the new to-do text as the payload.

c. Displaying the To-Do List:

We can use the useSelector hook from react-redux to access the state from the store and display it in our component:

import React from 'react';
import { useSelector } from 'react-redux';

const TodoList = () => {
  const todos = useSelector((state) => state.todos); // Select the 'todos' state

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>
          {todo.text}
          <button onClick={() => dispatch(removeTodo(todo.id))}>
            Remove
          </button>
        </li>
      ))}
    </ul>
  );
};

export default TodoList;

Here, the TodoList component uses useSelector to retrieve the todos array from the state and iterates over it to display each to-do item and its removal button, triggering the removeTodo action.

5. Conclusion:

This blog has provided a basic understanding of Redux and its integration with React using Redux Toolkit. By building the simple to-do list application, you've gained practical experience with the core concepts of Redux state management. Remember, this is just a basic example, and Redux can be used to manage complex state in large-scale applications effectively.

Muhaymin Bin Mehmood

About Muhaymin Bin Mehmood

Front-end Developer skilled in the MERN stack, experienced in web and mobile development. Proficient in React.js, Node.js, and Express.js, with a focus on client interactions, sales support, and high-performance applications.

Copyright © 2024 Mbloging. All rights reserved.