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>;
}- No dependency array: Runs on every render.
- Empty dependency array
[]: Runs only once when the component mounts. - With dependencies
[propOrState]: Runs on mount and whenever the specified dependencies change.
5. Preventing Unnecessary Updates
Sometimes components re-render when they don’t need to. You can optimize functional components using the following tools:
React.memo: A higher-order component that prevents a functional component from re-rendering if its props have not changed.useMemo: Memoizes expensive calculations so they are only re-computed when specific dependencies change.useCallback: Memoizes callback functions to prevent child components from re-rendering due to changing function references on every render.