React State Management

React State Management

Here are all the sections primarily covered in this blog:

  1. What is the state in react?
  2. How do you manage the state in the class components?
  3. How do you manage the state in functional components?
  4. Example of different types of hook build-in hooks.
  5. Context and Customtom hooks
  6. Redux
  7. Which factors will I consider for selection between redux and react context API?

# What is a state in react?

In React, a state is a built-in object that is used to store data that can change over time within a component. The state object is owned by the component and can be accessed and modified within the component's code. Whenever the state object is changed, the component will automatically re-render to reflect the new state.

A state can be used to store information that affects the behavior of the component, such as whether a button is disabled or enabled, whether a dropdown menu is open or closed, or whether a form input is valid or invalid. It can also be used to store data that is retrieved from an API or entered by the user.


# How to manage states in class components?

In object-oriented programming, a constructor is used to create an instance of a class, and the `this` keyword allows us to access the properties of the current object. The same concept applies to class components in React.

In React, we can initialize the initial state of a component using the constructor method, and manage state using the `this. state` object. We can update the state using `this.setState` method, which is provided by default in React class components.

Whenever setState is called, React automatically re-renders the component with the updated state. Finally, the render method is used to render the component with the current state. This allows us to build dynamic user interfaces that can respond to user input and change over time.


# How to manage state in a functional component?

For the functional components, React introduces an entirely new concept called React Hooks. By using different kinds of hooks we are able to manage state.

Those hooks are commonly used for state management include:

useState: The useState hook is the most commonly used hook for state management. It allows you to add a state to a functional component by declaring a state variable and a function to update it.

import React, { useState } from 'react'

function Example() {
  const [count, setCount] = useState(0);

  const handleButtonClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleButtonClick}>
        Click me
      </button>
    </div>
  );
};        


useEffect: This hook allows you to perform side effects in your component, such as updating the DOM or fetching data from an API. It can also be used to update the state based on changes in props or other state variables.

import React, { useState, useEffect } from 'react'

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
};        


useReducer: This hook allows you to manage the state using a reducer function, similar to how the state is managed in Redux. It can be useful for managing more complex states, especially if you have multiple related state variables that need to be updated together.

const initialState = { count: 0 }

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

export default Counter;;        


useContext: This hook allows you to access data that is stored in a Context object, which can be useful for sharing state between multiple components

import React, { useContext } from 'react'

const MyContext = React.createContext();

function MyComponent() {
  const myValue = useContext(MyContext);
  return <div>{myValue}</div>;
}

function App() {
  return (
    <MyContext.Provider value="Hello World">
      <MyComponent />
    </MyContext.Provider>
  );
}

export default App;        


useRef: This hook allows you to create a mutable reference to a DOM node or to a value that persists between renders, which can be useful for managing a state that doesn't trigger a re-render

import React, { useRef } from 'react'

function Example() {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
};        

While these hooks are often used for state management, they can also be used for other purposes. For example, useEffect can be used for cleanup operations, and useRef can be used to store a reference to a callback function.


# Custom Hooks:

Custom hooks allow you to remove reusable logic from your components. They are created by using the `use` prefix and can be used just like any other React hook. By using custom hooks, you can keep your code DRY (Don't Repeat Yourself) and make it easier to manage and maintain.

Here's an example of a custom hook that manages the state of a checkbox:

import { useState } from 'react'

function useCheckbox(initialValue = false) {
  const [isChecked, setIsChecked] = useState(initialValue);

  const toggleCheckbox = () => {
    setIsChecked(!isChecked);
  };

  return [isChecked, toggleCheckbox];
};        


In this example, the `useCheckbox` hook returns an array with two values: the current state of the checkbox (a boolean value), and a function to toggle the state. The `useState` hook is used to initialize the state with an initial value of false.

Custom hooks can also take in arguments, just like regular functions. For example, you could create a custom hook that fetches data from an API:

import { useState, useEffect } from 'react'

function useApi(url) {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
      } catch (error) {
        console.error(error);
      } finally {
        setIsLoading(false);
      }
    }

    fetchData();
  }, [url]);

  return [data, isLoading];
};        


In this example, the `useApi` hook takes in a URL as an argument and returns an array with two values: the fetched data (an object or array), and a boolean value indicating whether the data is still loading.

Custom hooks can be used in any component, just like any other React hook. For example, you could use the `useCheckbox` hook in a form component to manage the state of a checkbox:

import { useState } from 'react'
import useCheckbox from './useCheckbox';

function Form() {
  const [email, setEmail] = useState('');
  const [isChecked, toggleCheckbox] = useCheckbox(false);

  const handleSubmit = (event) => {
    event.preventDefault();
    // Submit the form data
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Email:
        <input type="email" value={email} onChange={(event) => setEmail(event.target.value)} />
      </label>
      <label>
        <input type="checkbox" checked={isChecked} onChange={toggleCheckbox} />
        Subscribe to our newsletter
      </label>
      <button type="submit">Submit</button>
    </form>
  );
};        


In this example, the Form component uses the `useCheckbox` hook to manage the state of a checkbox. The `isChecked` value is passed to the checked prop of the element, and the toggleCheckbox function is passed to the onChange prop.

By using custom hooks, you can make your components more modular and reusable, and reduce the amount of code duplication in your application.


# React Context API

React Context API is a powerful tool for managing state in React applications. It provides a way to share the state between components without having to pass it down through multiple levels of the component tree. With Context API, you can create a centralized store for your application's state, which can be accessed and modified by any component that needs it.

The Context API consists of two main parts: the context object and the context provider. The context object is created using the `createContext` method, which returns an object with two properties: Provider and Consumer. The Provider component is used to wrap the component tree that needs access to the shared state, while the Consumer component is used to access the shared state within a component.

To use the Context API for managing state, you first need to create a context object using the createContext method. For example:

import React, { createContext, useState } from 'react'

export const MyContext = createContext();

function App() {
  const [myState, setMyState] = useState('initial state');

  return (
    <MyContext.Provider value={{ myState, setMyState }}>
      <div>
        <h1>My App</h1>
        <ChildComponent />
      </div>
    </MyContext.Provider>
  );
}


;        


In this example, we have created a context object called MyContext, which contains a state variable called myState and a function to update it called `setMyState`. We have also wrapped the ChildComponent inside the `MyContext.Provider` component, which provides the shared state to all the child components.

To access the shared state within a component, you can use the `useContext` hook. For example:

import React, { useContext } from 'react'
import { MyContext } from './App';

function ChildComponent() {
  const { myState, setMyState } = useContext(MyContext);

  return (
    <div>
      <h2>Child Component</h2>
      <p>My state is: {myState}</p>
      <button onClick={() => setMyState('new state')}>
        Update State
      </button>
    </div>
  );
};        


In this example, we have imported the MyContext object from the App component and used the `useContext` hook to access the myState and `setMyState` variables. We can then use these variables to display and update the shared state within the child component.

Overall, the React Context API provides a simple and efficient way to manage the state in React applications. It allows you to create a centralized store for your application's state, which can be accessed and modified by any component that needs it. By using the `useContext` hook, you can easily access the shared state within your components and keep your code organized and modular.


# Redux

State management is one of the most important aspects of building a React application. As your application grows in complexity, so does your state. Redux is a state management library that helps to manage your application's state more effectively. In this article, we'll explore how Redux manages state in a React application.

Redux is based on three core principles:

  • Single source of truth: The state of your whole application is stored in an object tree within a single store.
  • The state is read-only: The only way to change the state is to emit an action, an object describing what happened.
  • Changes are made with pure functions: To specify how the state tree is transformed by actions, you write pure reducers.

Redux works by managing your application state in a central location called the store. The store holds the state of your application, and every component in your application can access the state from the store. When a component wants to update the state, it must dispatch an action to the store.

An action is a plain JavaScript object that describes what happened. It contains a type property that specifies the type of action being performed and an optional payload that contains any additional data that needs to be passed along with the action.

For example, if you have a todo application, the type of an action could be 'ADD_TODO' and the payload could be an object containing the todo item's text and ID.

Reducers are pure functions that specify how the state of your application should change in response to an action. They take the current state and an action as input and return a new state. Reducers should not modify the existing state but rather return a new state object. The returned state object should be a new object that represents the updated state of the application.

Here's an example of a reducer function that handles the 'ADD_TODO' action:

function todosReducer(state = [], action) 
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.payload.id,
          text: action.payload.text,
          completed: false
        }
      ];
    default:
      return state;
  }
}        


The store is created using the `createStore` function from the Redux library. It takes a reducer function as an argument and returns a store object. You can then use the store object to access the state and dispatch actions.

In a React application, you can use the connect function from the react-redux library to connect a component to the store. The connect function takes two arguments: `mapStateToProps` and `mapDispatchToProps`. `mapStateToProps` is a function that maps the state from the store to the props of your component, while `mapDispatchToProps` is a function that maps the dispatch function to the props of your component.

import { connect } from 'react-redux'

function TodoList({ todos, addTodo }) {
  return (
    <div>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
      <button onClick={() => addTodo('New Todo')}>Add Todo</button>
    </div>
  );
}

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

function mapDispatchToProps(dispatch) {
  return {
    addTodo: text =>
      dispatch({
        type: 'ADD_TODO',
        payload: {
          id: new Date().getTime(),
          text
        }
      })
  };
}

export default connect(mapStateToProps, mapDispatchToProps;        


# Which factors will I consider for selection between Redux and React Context API?

When deciding between Redux and React Context API for state management in a React application, there are a few factors to consider:

  • Size and complexity of the application: If the application is small and doesn't have complex state management requirements, React Context API might be a nice choice. However, for larger applications with complex state management requirements, Redux might be a better choice.
  • Scalability: Redux is designed to be scalable, so it can handle larger applications with ease. React Context API, on the other hand, might become unwieldy if the application grows too large.
  • Performance: React Context API is generally faster than Redux because it doesn't involve the overhead of the Redux store. However, for applications that require frequent updates and large amounts of data, Redux might be faster because it has a more efficient update mechanism.
  • Developer experience: React Context API is simpler to use and requires less boilerplate code than Redux. However, Redux has a well-defined structure and clear separation of concerns, which can make it easier for developers to reason about the application's state.
  • Community and ecosystem: Redux has a large and active community, with a wide range of plugins, middleware, and other tools available. React Context API is newer and has a smaller ecosystem, which might make it more difficult to find the resources and tools needed for a specific use case.

Ultimately, the choice between Redux and React Context API will depend on the specific requirements of the application and the preferences of the development team.

As a beginner-level learner of React, I wanted to share my thoughts on state management in React.

React is a popular JavaScript library that is widely used to build user interfaces. State management is an important aspect of React development because it enables the components to be dynamic and interactive. React provides various built-in ways to manage state, including useState, useEffect, useContext, useReducer, and more.

The `useState` hook is the simplest way to manage the state in a React component. It allows us to define and update the state within the component itself. useEffect is another popular hook that enables us to perform side effects like fetching data, subscribing to events, and more.

`React Context API` is an alternative to the traditional prop drilling approach, where we pass down states and methods through multiple layers of components. Context provides a way to pass data through the component tree without having to pass props down manually at every level.

The `useReducer` hook is another way to manage the state in a more structured and scalable way, especially in larger applications. It allows us to define a reducer function and dispatch actions to update the state.

Lastly, `redux` is an amazing third-party library for managing the state in React app.

In conclusion, choosing the right state management technique in React depends on various factors like application size, complexity, scalability, and developer experience.


I use different sources from the internet to complete the blogs. I hope this simple blog helps other beginners like me to understand the basics of React state management.

As this is my first blog, I can make mistakes and there is various place to improve myself. Please feel free to share your feedback and suggestions.

To view or add a comment, sign in

More articles by Abir Ahmed

  • A Practical Guide to Beginning ML Research

    Many juniors often ask me how they can begin a research-oriented career in machine learning. Most of them start…

    1 Comment

Others also viewed

Explore content categories