If you’re looking for a clear React hooks comparison, you’ve come to the right place. React hooks simplify state and side effect management, but knowing which hook to use and when can be confusing.
In this post, I’ll share a most commonly used React hook comparison so you’ll know exactly when to use each one.
Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!
Now, let’s jump right into it!
useState vs. useReducer: Choosing the Right React Hook for State
Use useState when:
- The state is simple, and you need to track one or two variables.
- Example: Counter, form input handling.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
);
}
Here, useState
is used for creating a simple counter. On button click, the setCount
function updates the state.
Use useReducer when:
- The state is complex, and you need to handle multiple state updates together.
- Example: Todo app, form validation.
import { useReducer, useState } from "react";
function todoReducer(state, action) {
switch (action.type) {
case "add":
return [...state, { text: action.payload, completed: false }];
case "toggle":
return state.map((todo, index) =>
index === action.index ? { ...todo, completed: !todo.completed } : todo
);
case "remove":
return state.filter((_, index) => index !== action.index);
default:
return state;
}
}
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
const [input, setInput] = useState("");
return (
<div>
<h2>Todo App using useReducer</h2>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={() => dispatch({ type: "add", payload: input })}>Add Todo</button>
<ul>
{todos.map((todo, index) => (
<li key={index} style={{ textDecoration: todo.completed ? "line-through" : "none" }}>
{todo.text}
<button onClick={() => dispatch({ type: "toggle", index })}>Toggle</button>
<button onClick={() => dispatch({ type: "remove", index })}>Remove</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
Here, useReducer
is used for complex state management. The dispatch function triggers the actions that modify the state.
useEffect vs. useLayoutEffect: React Hooks for Side Effects and Layout
Use useEffect when:
- You need to perform side effects after rendering the component.
- Example: API calls, event listeners, timers.
import { useEffect, useState } from "react";
function FetchData() {
const [data, setData] = useState(null);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => setData(json));
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
Here, useEffect
is used to fetch data from an API. This API sends the request after the component renders.
Use useLayoutEffect when:
- You need to modify something right after the DOM update.
- Example: Animation adjustments, measuring DOM elements.
import { useLayoutEffect, useRef } from "react";
function Box() {
const boxRef = useRef(null);
useLayoutEffect(() => {
console.log("Box width:", boxRef.current.offsetWidth);
});
return <div ref={boxRef} style={{ width: "100px", height: "100px", background: "red" }} />;
}
Here, useLayoutEffect
is used to measure the width of the element right after the DOM update.
Comparing React Hooks for Performance: useMemo, useCallback, and useRef
Use useRef when:
- You need to reference a DOM element directly.
- You need to track the state update without re-rendering.
import { useRef } from "react";
function InputFocus() {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} type="text" />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</>
);
}
Here, useRef
is used to directly refer to the input field so that on button click, the focus can be set.
Use useMemo when:
- You need to optimize expensive calculations.
- You need to avoid unnecessary re-renders.
import { useState, useMemo } from "react";
function ExpensiveComponent({ numbers }) {
function sum(numbers) {
console.log("Calculating sum...");
return numbers.reduce((acc, num) => acc + num, 0);
}
const [count, setCount] = useState(0);
const total = useMemo(() => sum(numbers), [numbers]); // Recalculates only when `numbers` change
return (
<div>
<p>Sum: {total}</p>
<button onClick={() => setCount(count + 1)}>Re-render: {count}</button>
</div>
);
}
export default function App() {
return <ExpensiveComponent numbers={[1, 2, 3, 4, 5]} />;
}
Here, useMemo
is used to cache expensive calculations so that this doesn’t recalculate on each render.
Use useCallback when:
- You need to memoize a function so that it doesn’t re-create unnecessarily.
- You need to prevent the child component from unnecessary re-renders.
import { useState, useCallback } from "react";
function ChildComponent({ handleClick }) {
console.log("Child re-rendered!"); // This will only re-render when function changes
return <button onClick={handleClick}>Click Me</button>;
}
export default function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("Button clicked!");
}, []); // Function reference will not change
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ChildComponent handleClick={handleClick} />
</div>
);
}
Here, useCallback
is used to memoize the function so that the Button component doesn’t re-render unnecessarily.
useContext
Use useContext when:
- You need to share the global state without props drilling.
- Example: Theme toggle, user authentication.
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Toggle Theme</button>
);
}
Here, useContext
is used to share a global state (theme) so that multiple components can access the state without prop drilling.
Comparison Table
Hook | When to use? |
---|---|
useState | For simple state |
useReducer | For complex state logic |
useEffect | For side effects and async tasks |
useLayoutEffect | For UI modifications after DOM updates |
useRef | For DOM reference and mutable values |
useMemo | For optimizing expensive calculations |
useCallback | For optimizing function re-creation |
useContext | For global state management |
Using the right React hook is important for code maintainability and performance optimization.
🎯Wrapping Up
That’s all for today!
For paid collaboration connect with me at : connect@shefali.dev
I hope this post helps you.
If you found this post helpful, here’s how you can support my work:
☕ Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.
Keep coding & happy learning!
Top comments (0)