How to Update State with React Hooks

React Hooks allow you to manage and update state inside functional components without writing class components. This article explains how to update state values using the useState and useReducer hooks, how to handle functional state updates, and how to trigger side effects when hook values change.

Updating Basic State with useState

The useState hook is the primary tool for managing state in React functional components. It returns an array with two elements: the current state value and a setter function to update that value.

To update the state, you call the setter function with the new value:

import React, { useState } from 'react';

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

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

Using Functional Updates

If your new state depends on the previous state, you should pass a function to the setter instead of a direct value. This prevents bugs related to asynchronous state batching.

// Safely updating state based on previous state
setCount(prevCount => prevCount + 1);

Updating Complex State with useReducer

For complex state logic where the next state depends on multiple actions or nested objects, useReducer is the preferred hook. It works by dispatching actions to a reducer function, which then determines the new state.

To update state with useReducer, dispatch an action object containing a type and an optional payload:

import React, { useReducer } from 'react';

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 (
    <>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

Triggering Side Effects on Update

When you update a Hook’s state, you often need to run side effects (such as API calls, subscriptions, or manual DOM manipulations) in response. The useEffect hook handles this by accepting a dependency array.

To execute code whenever a specific state value updates, include that state variable in the dependency array:

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

function UserProfile({ userId }) {
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    // This runs every time 'userId' changes
    fetch(`https://api.example.com/user/${userId}`)
      .then(res => res.json())
      .then(data => setUserData(data));
  }, [userId]); // Dependency array
}