How to Update Functional Components in React

In React, functional components are updated primarily by managing state changes, receiving new props, or consuming updated context values. This article provides a direct guide on how to trigger and handle updates in React functional components using hooks like useState, useReducer, and useEffect, as well as how to optimize these updates for better application performance.

1. Updating State with the useState Hook

The most common way to update a functional component is by modifying its internal state. The useState hook returns a state variable and a setter function. When you call the setter function with a new value, React schedules a re-render of the component.

import React, { useState } from 'react';

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

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

If the next state depends on the previous state, you should pass a updater function to the setter to avoid stale state bugs:

setCount(prevCount => prevCount + 1);

2. Managing Complex Updates with useReducer

For components with complex state logic or when the next state depends on multiple previous states, the useReducer hook is the preferred tool. It uses a dispatch action mechanism similar to Redux. Calling dispatch triggers a state change and updates the component.

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 CounterWithReducer() {
  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>
  );
}

3. Updating via Props from Parent Components

Functional components automatically re-render whenever they receive new props from their parent component. You do not need to write any extra code to handle this; React detects changes in the passed properties and updates the DOM accordingly.

// Parent Component
function Parent() {
  const [text, setText] = useState("Hello");
  return <Child message={text} />;
}

// Child Component - automatically updates when `message` changes
function Child({ message }) {
  return <h1>{message}</h1>;
}

4. Running Code After an Update with useEffect

To perform side effects (such as data fetching, manual DOM manipulations, or logging) after a component updates, use the useEffect hook. By specifying variables in the dependency array, you control exactly when the effect runs.

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

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // This runs on mount and every time `userId` updates
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => response.json())
      .then(data => setUser(data));
  }, [userId]); // Dependency array

  return user ? <div>{user.name}</div> : <div>Loading...</div>;
}

5. Preventing Unnecessary Updates

Sometimes components re-render when they don’t need to. You can optimize functional components using the following tools: