DEV Community

Cover image for “When Not to Use useMemo, useCallback, and useReducer in React”
dani orooji
dani orooji

Posted on

“When Not to Use useMemo, useCallback, and useReducer in React”

React gives us some amazing hooks like useCallback, useMemo, and useReducer. They’re powerful, but here’s the catch: using them in the wrong place can actually make your app slower and harder to maintain.
In this post, I’ll break down:

  • What each hook does
  • When you should use it
  • When you shouldn’t use it (common anti-patterns)
  • Better real-world examples

🔹 useCallback

✅ What it does
Keeps a function reference stable between renders. Useful when passing callbacks to memoized child components.

❌ When NOT to use it
On simple inline functions (onClick={() => setOpen(true)})
When you’re not passing the function to a child component
If the child isn’t wrapped in React.memo
✅ Good example

import { useCallback, useState, memo } from "react";

const Button = memo(({ onClick }) => {
  console.log("Button rendered");
  return <button onClick={onClick}>Click Me</button>;
});

export default function App() {
  const [count, setCount] = useState(0);

  // ✅ useCallback prevents function recreation on each render
  const handleClick = useCallback(() => {
    console.log("Clicked!");
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Button onClick={handleClick} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Without useCallback, the would re-render every time count changes.
🔹 useMemo
✅ What it does
Memoizes the result of a calculation so it’s only recomputed when dependencies change.
❌ When NOT to use it

  • For cheap operations (a + b)
  • For small lists or trivial filtering/sorting
  • Just “because” (it adds overhead itself) ✅ Good example
import { useMemo, useState } from "react";

export default function App() {
  const [filter, setFilter] = useState("");
  const [items] = useState(
    Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`)
  );

  // ✅ Expensive filtering only runs when "filter" changes
  const filteredItems = useMemo(() => {
    console.log("Filtering...");
    return items.filter((item) => item.includes(filter));
  }, [filter, items]);

  return (
    <div>
      <input
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Search..."
      />
      <ul>
        {filteredItems.slice(0, 10).map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

If you filter a huge list without useMemo, React would recompute the filter on every keystroke, even if unrelated state changes.
🔹 useReducer
✅ What it does

Alternative to useState for managing complex state with multiple transitions.
❌ When NOT to use it

  • When state is simple (isOpen, count, inputValue)
  • When you don’t need multiple actions or a reducer function
  • For small components where useState is enough ✅ Good example
import { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "add":
      return { ...state, todos: [...state.todos, action.payload] };
    case "remove":
      return {
        ...state,
        todos: state.todos.filter((t, i) => i !== action.index),
      };
    default:
      return state;
  }
}

export default function App() {
  const [state, dispatch] = useReducer(reducer, { todos: [] });

  return (
    <div>
      <button onClick={() => dispatch({ type: "add", payload: "Learn Hooks" })}>
        Add Todo
      </button>
      <ul>
        {state.todos.map((todo, i) => (
          <li key={i}>
            {todo}{" "}
            <button onClick={() => dispatch({ type: "remove", index: i })}>
              ❌
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

For complex state transitions like a todo list, shopping cart, or form wizard, useReducer shines. For a simple counter? useState is enough.
💡What about you? Have you ever overused these hooks and then realized they were unnecessary? Share your experience in the comments!

Top comments (0)