React Hooks Showdown: useMemo vs useCallback vs useState vs useEffect

Back to Index
gist//15/12/2025//3 Min Read//Updated 15/12/2025

1. useState: The Memory


What it does: Allows a functional component to "remember" information between renders.

When to use: Whenever you have data that changes over time and needs to trigger a re-render to update the UI (e.g., form inputs, toggle states, counters).

javascript
const [count, setCount] = useState(0); // Update state setCount(count + 1);

2. useEffect: The Side Effect


What it does: Performs side effects in functional components. "Side effects" are things like data fetching, subscriptions, or manually changing the DOM.

When to use: When you need to do something after the component renders or when a specific value changes.

javascript
useEffect(() => { // This runs after every render document.title = `You clicked ${count} times`; // Optional cleanup mechanism return () => { // Clean up code here }; }, [count]); // Only re-run if 'count' changes

3. useMemo: The Calculator


What it does: Memoizes (caches) the result of a calculation. It only re-calculates the value when one of its dependencies changes.

When to use: Optimization. Use it to avoid expensive calculations on every render.

javascript
const expensiveValue = useMemo(() => { return computeExpensiveValue(a, b); }, [a, b]); // Only re-compute if 'a' or 'b' changes

Note: Don't overuse this. Memoization has its own cost.

4. useCallback: The Function Saver


What it does: Memoizes a function definition. It returns the same function instance between renders unless its dependencies change.

When to use: Optimization. Primarily useful when passing callbacks to optimized child components (like those wrapped in React.memo) to prevent unnecessary re-renders of the child.

javascript
const handleClick = useCallback(() => { doSomething(a, b); }, [a, b]); // Function identity remains stable unless 'a' or 'b' changes

Summary Table


HookReturnsPurposeRe-runs when...
useState[state, setter]Manage stateSetter is called
useEffectundefinedSide effectsDependencies change
useMemoCalculated ValueCache expensive calculationDependencies change
useCallbackMemoized FunctionStable function identityDependencies change

Key Difference: useMemo vs useCallback


  • useMemo caches the result of a function call.
  • useCallback caches the function itself.

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

Real World Example: Language Switching


A common question is: "Why use useEffect for fetching data when useState holds it?"

Let's say we have a Language Switcher (EN/TR).

The Wrong Way (Trying to use useState for fetching)


javascript
// This won't work because fetch is async and returns a Promise, not the data immediately. const [books] = useState(fetch(`/stories/books_${language}.piml`));

The Right Way (useEffect + useState)


  1. State holds the result (the parsed books).
  2. Effect handles the process (fetching when language changes).
javascript
const { language } = useContext(DndContext); // "en" or "tr" const [books, setBooks] = useState([]); // Holds the data // Run this side effect whenever 'language' changes useEffect(() => { const fetchData = async () => { // 1. Fetch the file based on the dynamic language variable const response = await fetch(`/stories/books_${language}.piml`); // 2. Parse the result const text = await response.text(); const data = parsePiml(text); // 3. Update state (triggers re-render) setBooks(data.books); }; fetchData(); }, [language]); // <--- The dependency that triggers the re-fetch

This pattern ensures that every time the user clicks "TR", the effect re-runs, fetches the Turkish content, updates the state, and the UI refreshes automatically.

Analyzing data structures... Delicious.