A step-by-step Guide to using Redux for State Management in React
Tags: webdev, javascript, react, redux
Published: 2023-02-10 00:28:26

Redux is a state management tool for JavaScript applications.
It provides a store for our applications and methods to get the state, listen to changes in your state, and update it.
In this article, you will learn how to use Redux in your React application by creating a simple todo application.



## Repository

Check out the complete source code [here](https://github.com/Adeku5080/todo-app-with-react-redux)



## Prerequisites

The todo Application will be built with React and Redux.

- Basic Javascript Knowledge


- Node installed on your device


- Basic understanding of React


## Three Main Parts of Redux

Redux contains three main parts:


- Store: a state container that holds all the application state and provides methods to interact with the state.






- Actions: a plain object describing the change that occurred in your application. It must contain a type field indicating the action being performed.


- Reducers: reducer is a function that takes the current state and the action which occurred and returns a new state.



## Setting up Redux in a React application

**Step 1: Install Redux and react-redux**


```javascript
    npm install redux react-redux
```


**Step 2: Create a Redux folder in your src directory**


The `src/redux` directory will contain your reducers, actions, and store.


**Step 3: Create a todo folder in your redux folder**

The `src/redux/todo` will contain your `todoActions.js` and `todoReducer.js` files.


**Step 4:  Create your actions**

For each action object, add the type of the action and the payload, if necessary. 

`src/redux/todo/todoActions.js`


    

```javascript
export const addTask = (data) => ({
      type: "ADD_TASK",
      payload: data,
    });
    
    export const fetchTasks = (data) => ({
      type: "FETCH_TASK",
      payload: data,
    });
    
    export const deleteTasks = (task) => ({
      type: "DELETE_TASK",
      payload: task,
    });
    
    export const updateTasks = (task) => ({
      type: "UPDATE_TASK",
      payload: task,
    });

```

**step 5: Create your reducers**

Define the initial state of your todo application and create a Reducer function that takes in your initial state and action as parameters. State in react is immutable, meaning it should not change directly. So for every condition, a copy of the state is first made using the spread operator  `...state`.

`src/redux/todo/todoReducers.js`


    

```javascript
const INITIAL_STATE = {
      todos: [],
    };
    
    const todoReducer = (state = INITIAL_STATE, action) => {
      switch (action.type) {
        case "ADD_TASK":
          return {
            ...state,
            todos: [...state.todos, action.payload],
          };
    
        case "FETCH_TASK":
          return {
            ...state,
            todos: action.payload,
          };
    
        case "UPDATE_TASK":
          return {
            ...state,
            todos: state.todos.map((todo) => {
              if (todo.id === action.payload) {
                todo.title = action.payload.title;
              }
            }),
          };
    
        case "DELETE_TASK":
          return {
            ...state,
            todos: state.todos.filter((todo) => todo.id !== action.payload),
          };
        default:
          return state;
      }
    };
    
    export default todoReducer;

```

**step 6: Create your root-reducer**

As the application grows more complex, it can contain multiple reducers which manage independent parts of your state; with the help of the combineReducers helper function provided by the Redux library, you can import all the reducers in the application, and export a single root reducer file.

`src/redux/root-reducer.js`


  

```javascript
  import { combineReducers } from "redux";
    import todoReducer from "./todo/todoReducer";
    
    const rootReducer = combineReducers({
      todo: todoReducer,
    });
    
    export default rootReducer;
```


**Step 7: Create the store**

Create a store.js file in the `src/redux`  folder. Add a middleware called redux-logger to help log your state changes in the console.

`src/redux/store.js`


   

```javascript
 import { createStore, applyMiddleware } from "redux";
    import logger from "redux-logger";
    import rootReducer from "./root-reducer";
    
    const middlewares = [];
    
    if (process.env.NODE_ENV === "development") {
      middlewares.push(logger);
    }
    
    export const store = createStore(rootReducer, applyMiddleware(...middlewares));
    
    export default store;
```


**step 8: Connect your Redux store to your application**

Connecting your Redux store to your application requires wrapping your main app component in your `src/index.js` file with a Provider component, and passing your store into the provider component like a prop.

`src/index.js`


 

```javascript
   import { Provider } from "react-redux";
    import store from "./Redux/store";
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      <Provider store={store}>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </Provider>
    );
```


Create a demo rest JSON web service using json-server and a `db.json` file with sample data in the `src` folder.
Redux has libraries like Redux-thunk for performing asynchronous tasks and data fetching, but handling asynchronous tasks in this application will be done in components.

Install json webserver


```javascript
   npm install -g json-sever

```

**Using Redux from the components**

Now that you have set up the redux store, you can consume state and dispatch actions using useSelector and useDispatch hooks.

This form is the input form to create a task

`AddForm.jsx`


   

```javascript
  <form onSubmit={handleSubmit}>
            <input
              name="task"
              placeholder="Add a task"
              type="task"
              value={task}
              onChange={handleChange}
            />
            <button>Add task</button>
          </form>
```

And in the handleSubmit handler function,  make a request to the demo web service to add a task and dispatch our `addTask` action.

`handleSubmit`


```javascript
     const handleSubmit = async (e) => {
        e.preventDefault();
    
        const {data} = await axios.post("http://localhost:4000/tasks", {
          title: task,
        });
        dispatch(addTask(data));
    
      };
```


**Fetch All Tasks**

Fetch all tasks in the app.js component and pass them to the `AddForm.jsx` component as a prop for it to be rendered.
In the useEffect hook, send a get request to your endpoint to get all the tasks and dispatch a fetchTasks action with the data obtained from the response as the payload.


 

```javascript
   useEffect(() => {
        async function fetchData() {
          const { data } = await axios.get("http://localhost:4000/tasks");
          dispatch(fetchTasks(data));
        }
        fetchData();
      }, []);
```


**Deleting a Task**

To delete a task,  create a `handleDelete`  function and in it, send a delete request to your endpoint.

`delete task`


   

```javascript
   const handleDelete = async (id) => {
        const res = await axios.delete(`http://localhost:4000/tasks/${id}`);
        dispatch(deleteTasks(id));
      };
```


**Updating a Task**

To update a task,  create an input form in which you add your new update and also a `handleUpdate`  function that takes care of sending a patch request to the endpoint.

`UpdateForm.jsx`


 

```javascript
         <form onSubmit={handleSubmit}>
            <input
              name="task"
              placeholder="Add a task"
              type="task"
              value={task}
              onChange={handleChange}
            />
            <button onClick={()=>handleUpdate(params.id)}>Update task</button>
          </form>
```

`handleUpdate`


```javascript
      const handleUpdate = async (id) => {
    
        const response = await axios.patch(`http://localhost:4000/tasks/${id}`, {
          title: task,
        });
        dispatch(updateTasks(task))
      };
```


After completing the application, your demo application will look like this: 


[Demo of the todo app](https://www.dropbox.com/s/r1xr4wb03ewqmih/screen-capture%20%283%29%20%28online-video-cutter.com%29%20%281%29.mp4?dl=0)



## Conclusion

When working with small-scale applications, using Redux for state management adds an extra layer of complexity that is not necessary since the state does not need to be passed down to many child components.

For large-scale applications, managing state with Redux provides a single source of truth, making it easier to understand data flow throughout the application and providing state value to different components in our application.


## Resources

[Redux essentials](https://redux.js.org/tutorials/essentials/part-1-overview-concepts)
[What is Redux?](https://www.freecodecamp.org/news/what-is-redux-store-actions-reducers-explained/)