Introduction to React Redux

Introduction to React Redux

ยท

7 min read


Introduction

Modern web applications have become increasingly complex, with a myriad of states to manage. From user preferences, data loading status, and authentication tokens, managing all these without a reliable state management solution can lead to a mess. This is where React Redux steps into the limelight. ๐Ÿ˜ƒ

React Redux, often simply referred to as Redux, is a predictable state container for JavaScript/TypeScript applications. It works hand-in-hand with React (although not exclusively) to provide an elegant solution to state management in your applications.


Why Do We Need React Redux?

To truly grasp the importance of Redux, let's first understand the challenges developers face without it.

  1. Prop Drilling: In large React applications, passing data between components can become tedious. This phenomenon, known as prop drilling, is when you pass data through multiple components that don't necessarily need the data, but are simply intermediaries.

  2. Inconsistent States: Without a single source of truth, different components can have different states leading to inconsistances throughout the application.

  3. Difficult Debugging: When state changes are scattered across various components, tracking down a bug or a faulty state change can be like finding a needle in a haystack.

React Redux offers solutions to these challenges by providing the following:

  • A Single Source of Truth: By maintaining the entire application state in one central location, Redux ensures consistency and coherence.

  • Predictable State Changes: Redux enforces the use of pure functions, called reducers, to handle state changes. This means that given an action and a state, your next state is entirely predictable.

  • Powerful Developer Tools: Redux's developer tools allow you to travel back in time by inspecting past states, making debugging a lot easier.


The Core Principles of Redux

Redux operates on three fundamental principles:

  1. Single Source of Truth: The state of the entire application is stored in a single JavaScript object within a single store.

  2. State is Read-Only: The only way to change the state is by emitting an action, an object describing what happened. This ensures that neither views nor network callbacks will ever write directly to the state.

  3. Changes Are Made Using Pure Functions: To specify how state transforms in response to actions, you write pure reducers.


How Does Redux Work?

At a high level, the Redux flow can be described as follows:

  1. Action Dispatch: An event occurs, like a user clicking a button. This dispatches an action, a plain JavaScript object describing the event.

  2. Reducers: Reducers are pure functions that take the previous state and an action, and return a new state. They determine how the application's state changes in response.

  3. Store: The new state is saved in the Redux store, a single JavaScript object.

  4. View Updates: The React components "listen" to the store, and when the state they rely on changes, they automatically re-render.


Connecting React With Redux

To integrate Redux with a React application, the library "react-redux" is used. It provides two crucial components:

  • Provider: This wraps around the main App component and makes the Redux store accessible to any nested components.

  • connect(): This function connects a React component to the Redux store. It allows the component to dispatch actions to the store and read from its state,


Why Should You Use React Redux?

  1. Scalability: As applications grow, so does their complexity. Redux provides a structured way to manage state that scales well with your application.

  2. Community and Ecosystem: Redux has a vast community, which means a plethora of resources, middleware, and tools available to help streamline your development process.

  3. Flexibility: While Redux is most commonly used with React, it's not limited to it. This means if you have other projects or want to migrate. Redux can still be your go-to for state management.

  4. Improved Developer Experience: With hot-reloading and time-travel debugging. Redux significantly improves the developer experience, making it easier to test and debug applications.


Implementing Redux into a React App

Now that we've gone over the benefits of React Redux let's create a new app implementing React Redux. We will create a simple TODO task manager, first create a new React app using the following command:

npx create-react-app redux-demo

This will create a new React app called redux-demo, next move into the app directory and install React Redux using the following commands:

cd redux-demo
npm install redux react-redux @reduxjs/toolkit

Next, we need to set up the Redux store, create a new folder called "redux" inside the "src" directory. Inside "redux" create a new file called "store.js" and populate it with the following:

// src/redux/store.js
import { configureStore } from '@reduxjs/toolkit';

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

We will implement the reducer later. Next we will define a Redux Slice for adding tasks, toggling tasks and deleting tasks. Inside the "redux" folder, create new file called "tasksSlice.js" and fill it with the following:

// src/redux/tasksSlice.js
import { createSlice } from '@reduxjs/toolkit';

export const tasksSlice = createSlice({
  name: 'tasks',
  initialState: [],
  reducers: {
    addTask: (state, action) => {
      state.push({ id: Date.now(), text: action.payload, completed: false });
    },
    toggleTask: (state, action) => {
      const task = state.find(task => task.id === action.payload);
      if (task) {
        task.completed = !task.completed;
      }
    },
    deleteTask: (state, action) => {
      return state.filter(task => task.id !== action.payload);
    }
  }
});

export const { addTask, toggleTask, deleteTask } = tasksSlice.actions;
export default tasksSlice.reducer;

Now that we have created the reducer we can now add it it our store file like follows:

// src/redux/store.js
import { configureStore } from '@reduxjs/toolkit';
import tasksReducer from './tasksSlice';

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

In the above I have imported the tasks reducer and added it to the reducer object.

Next we will create two components, one to add a new task and one to list tasks. Create a new folder called "components" inside the "src" folder, create a new file called "TaskInput.js" inside the components directory and populate it with the following:

// src/components/TaskInput.js
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { addTask } from '../redux/tasksSlice';

function TaskInput() {
  const [input, setInput] = useState('');
  const dispatch = useDispatch();

  const handleSubmit = () => {
    if (input) {
      dispatch(addTask(input));
      setInput('');
    }
  };

  return (
    <div>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={handleSubmit}>Add Task</button>
    </div>
  );
}

export default TaskInput;

The above component allows us to add tasks, when the button is clicked an event is dispatched and the store is updated with a new task. Finally we need a component to list the TODOs, create a new file called "TaskList.js" inside the components directory and populate it with the following:

// src/components/TaskList.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { toggleTask, deleteTask } from '../redux/tasksSlice';

function TaskList() {
  const tasks = useSelector((state) => state.tasks);
  const dispatch = useDispatch();

  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>
          <span
            style={{ textDecoration: task.completed ? 'line-through' : 'none' }}
            onClick={() => dispatch(toggleTask(task.id))}
          >
            {task.text}
          </span>
          <button onClick={() => dispatch(deleteTask(task.id))}>Delete</button>
        </li>
      ))}
    </ul>
  );
}

export default TaskList;

Done! Finally, we need to edit the App.js to reflect all of these changes. Open up "src/App.js" and edit it to the following:

// src/App.js
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './redux/store';
import TaskInput from './components/TaskInput';
import TaskList from './components/TaskList';

function App() {
  return (
    <Provider store={store}>
      <div className="App">
        <h1>Task Manager</h1>
        <TaskInput />
        <TaskList />
      </div>
    </Provider>
  );
}

export default App;

Now all we have to do it start the application via:

npm run start

You should be redirected to the web browser with a simple app showing. You should also be able to add tasks, toggle tasks and delete tasks.


Conclusion

React Redux offers a structured, scalable and maintainable approach to state management in web applications. Through its principles of a single source of truth, read-only state and pure functions, Redux ensures predictability and consistency. Whether you're a novice or a seasoned developer, integrating Redux into your React applications can profoundly enhance their robustness and your development experience.

I hope this guide has taught you something and have fun trying out Redux in your application. Feel free to improve on the above examples UI. Or maybe even add API calls.

As always you can find the sample code on my Github: https://github.com/ethand91/redux-demo

Happy Coding! ๐Ÿ˜Ž


Like me work? I post about a variety of topics, if you would like to see more please like and follow me. Also I love coffee.

โ€œBuy Me A Coffeeโ€

If you are looking to learn Algorithm Patterns to ace the coding interview I recommend the following course

If you are interested in using a VPN, I recommend ExpressVPN. If you use the below link you get 30 days for free

Did you find this article valuable?

Support Ethan by becoming a sponsor. Any amount is appreciated!

ย