DEV Community

Odukwe Precious Chiemeka
Odukwe Precious Chiemeka

Posted on

How To Handle State Management in Your React Application Using Redux

State management is an essential aspect of any React application, as it determines how the different components of the app interact with each other and with the data they display. When developing large React apps, state management can be difficult and time-consuming, but libraries are available to help you handle your state management more effectively. One of these libraries is Redux.

Redux is a library that helps organize and manage the state of a React app in a centralized and predictable way. Redux provides a centralized store for managing the state of a React application. The store holds the application's state and allows components to access and update it. This article provides information to help you handle state management effectively using Redux.

Setting Up Redux in Your Application

To use redux in a React application, you must first install the library by running the following command in your project’s directory on the terminal:

npm install redux
Enter fullscreen mode Exit fullscreen mode

Alternatively, you run this command if you are using yarn

yarn add redux
Enter fullscreen mode Exit fullscreen mode

After installing redux, you install the react-redux library. The react-redux library is a library that binds React and Redux. It allows developers to use the Redux store and its functionality in a React application. The react-redux library makes it easier to connect React components to the Redux store and manage the application's state.

To install react-redux, run the following command:

npm install react-redux
Enter fullscreen mode Exit fullscreen mode

Run this command, if you are making use of yarn

yarn add react-redux
Enter fullscreen mode Exit fullscreen mode

Creating a Store Using the createStore Function

After installing the redux library, you need to create a store. A store holds the state of your application and allows the components to access and update it. You make the store using the createStore function of the redux library. The createStore function takes in three arguments, two are optional, and one is not. The two optional arguments are the initial state and an enhancer, and the non-optional argument is a reducer function.

A reducer function is a function that takes the current state of the application and returns the new state. The reducer updates the application’s state based on the dispatched actions. A reducer function takes two arguments state and action. The state argument is the current state of the application, and the action argument is an object that describes the action that occurred, including the type of the action.

An enhancer is a higher-order function that allows you to add functionality to the store, such as middleware for handling async actions or logging. A typical example of an enhancer is middleware. A middleware is a function that takes the store.dispatch method as an argument and returns a new function that can be used to dispatch actions. Examples of enhancers are the redux-thunk middleware, redux-logger, redux-devtools-extension, etc.

import { createStore } from 'redux';

const initialState = {
  counter: 0
};

function reducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {
        counter: state.counter + 1
      };
    case 'DECREMENT':
      return {
        counter: state.counter - 1
      };
    default:
      return state;
  }
}

const store = createStore(reducer, intialState);

export default store;
Enter fullscreen mode Exit fullscreen mode

In the code block above, you import the createStore function from the redux library and use it to create a store. The initialState is an object containing the counter property. The state argument of the reducer function is set to initialState by default.

The reducer function uses a switch statement to determine what to do based on the value of the action.type property. If the value of action.type is 'INCREMENT', the function returns a new object with the counter property set to the current value of the counter plus 1. If the value of action.type is 'DECREMENT,’ the function returns a new object with the counter property set to the current value of the counter minus 1.

To create actions, you will create a function mapDispatchToProps that takes the dispatch method as an argument. Then using the dispatch method, you will dispatch actions to the store.

For example:

export function mapDispatchToProps(dispatch) {
 return {
    increment: () => dispatch({type: 'INCREMENT'}),
    decrement: () => dispatch({type: 'DECREMENT'})
 }       
}
Enter fullscreen mode Exit fullscreen mode

The mapDispatchToProps function returns an object with two properties, increment, and decrement, each of which is a function that dispatches an action to the store. Calling the increment function will dispatch an action with the type 'INCREMENT' to the store. Calling the decrement function will dispatch an action with the type ‘DECREMENT’ to the store.

Connecting Your Component to the Redux Store Using the Connect Function

To connect a component to the store and access the state, you use the connect function from the react-redux library. The connect function takes two arguments: a function that maps the store's state to the props of the component and an object that maps action creators to the props of the component.

For example:

import { connect } from 'react-redux';
import { mapDispatchToProps } from './actions';

function Counter(props) {
  return (
    <div>
      <h1>{props.counter}</h1>
      <button onClick={props.increment}>Increment</button>
      <button onClick={props.decrement}>Decrement</button>
    </div>
  );
}

function mapStateToProps(state) {
  return {
    counter: state.counter
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Counter);
Enter fullscreen mode Exit fullscreen mode

The connect function takes two arguments: the mapStateToProps function that maps the store's state to the props of the component and the mapDispatchToProps function that returns an object that maps action creators to the props of the component. The component is then wrapped in a higher-order component (HOC) that handles the store interaction, and the resulting HOC component is returned. The component can access the state and action it needs from the store via its props.

Now you have created a store and connected your component to the store. However, for your store to be accessible to all your React components, you must utilize the provider component from the react-redux library.

To ensure your store is available throughout your application, you import the provider component from the react-redux library into your app component. The provider component takes the store as a prop and makes it available to all child components. To ensure your entire application has access to the store, you wrap all your components within the provider component.

Like so:

import { Provider } from 'react-redux';
import store from './store';
import Counter from './counter';

function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Handling Asynchronous Operations Using Redux-Thunk Middleware

The redux-thunk middleware is a middleware for Redux that allows you to write action creators that return a function instead of an action object. In a Redux application, action creators return an action object with a type and an optional payload property. These action objects are dispatched to the store and passed to the reducer function, which updates the application’s state.

However, in some cases, you may need to perform async operations, such as making an API call, before dispatching an action. In these cases, you use redux-thunk to write action creators that return a function instead of an action object. This function can then perform the async operation and dispatch the action.

Installing and Setting Up Redux-Thunk

To use the redux-thunk middleware, you need to install it in your application. To install the middleware, run the following command in your project’s directory on the terminal:

npm install redux-thunk
Enter fullscreen mode Exit fullscreen mode

After installing, you import the thunk function from the middleware and pass it as an argument to the applyMiddleware function of the redux library. The applyMiddleware function is a function from the redux library that allows you to apply middleware to a Redux store.

Like so:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { reducer } from './reducer';

const middleware = [thunk]; 

const store = createStore(reducer, applyMiddleware(...middleware));

export default store;
Enter fullscreen mode Exit fullscreen mode

In the code block above, the createStore function is invoked with the reducer function and the applyMiddleware function. The applyMiddleware function can take multiple middlewares as arguments.

After creating the store, you create your reducer and actions:

export function fetchData() {
  return function (dispatch) {
        fetch('https://jsonplaceholder.typicode.com/posts')
    .then( (response) => response.json() )
    .then( (data) => dispatch({
        type: 'FETCH_POSTS',
        payload: data
    }) )
    }
}
Enter fullscreen mode Exit fullscreen mode

You created an action called fetchData that, when called, dispatches an action of type FETCH_POSTS with the payload of the data returned by the API.

Now, after creating your action, you proceed to create your reducer function:

const initialState = {
  data: [],
};

export function reducer(state = initialState, action) {
  switch (action.type) {
    case 'FETCH_POSTS':
      return {
        data: action.payload,
      };
    default:
      return state;
  }
}
Enter fullscreen mode Exit fullscreen mode

In the code block above, the initialState is an object holding a single property, data. The function uses the switch statement to check the type property of the action object.

In this case, the only action type handled is FETCH_POSTS. If the action's type is FETCH_POSTS, it will return a new state object with the data property set to the action’s payload. The action’s payload is the data that was fetched.

After creating your store, you connect your component to the store.

Like so:

import React from 'react';
import { connect } from 'react-redux';
import { fetchData } from './actions';

function Home(props) {

    React.useEffect( props.fetchData, [] );

    return(
        { props.data.map( (item) => ( <p key={item.id}>{item.body}</p> )) }
    )
}

const mapStateToProps = (state) => ({
    data: state.data
})

export default connect( mapStateToProps, { fetchData })(Home);
Enter fullscreen mode Exit fullscreen mode

Conclusion

Redux is a powerful library that can help you handle state management in your React application. By using a centralized store, actions, and reducers, you can effectively manage the state of your application. By connecting your React components to the store using the react-redux library, you can easily access the state and dispatch actions from within your components. The middleware redux-thunk help in handling the async flow of the actions.

With the help of this article, you should now have a good understanding of how to set up and use Redux in your React application and how it can help you handle state management in a more efficient and manageable way.

Top comments (2)

Collapse
 
phryneas profile image
Lenz Weber

I'm sorry to tell you that, but your article describes a very outdated style of Redux - since 2019 Redux looks very different from that. No more createStore, ACTION_TYPES, immutable switch..case reducers or connect/mapStateToProps. Please give redux.js.org/introduction/why-rtk-... a read.

Collapse
 
precious654 profile image
Odukwe Precious Chiemeka

Thank you, I would look into it