Curriculum
Course: React
Login

Curriculum

React

Text lesson

useCallback

The useCallback Hook in React returns a memoized version of a callback function.

Think of memoization as caching a value to avoid recalculating it.

This approach helps isolate resource-intensive functions, preventing them from running on every render. The useCallback Hook runs only when one of its dependencies updates, which can enhance performance.

The useCallback and useMemo Hooks are similar, with the key difference being that useMemo returns a memoized value, while useCallback returns a memoized function. You can explore useMemo further in its dedicated chapter.

Problem

Using useCallback helps prevent unnecessary re-renders of a component, ensuring it only re-renders when its props change, such as when todos change.

This example is similar to the one discussed in the React.memo section.

Example:

index.js

import { useState } from "react";
import ReactDOM from "react-dom/client";
import Todos from "./Todos";

const App = () => {
 const [count, setCount] = useState(0);
 const [todos, setTodos] = useState([]);

 const increment = () => {
    setCount((c) => c + 1);
 };
 const addTodo = () => {
    setTodos((t) => [...t, "New Todo"]);
 };
 return (
    <>
      <Todos todos={todos} addTodo={addTodo} />
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>+</button>
      </div>
    </>
 );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

 

Todos.js

import { memo } from "react";

const Todos = ({ todos, addTodo }) => {
  console.log("child render");
 return (
    <>
      <h2>My Todos</h2>
      {todos.map((todo, index) => {
        return <p key={index}>{todo}</p>;
      })}
      <button onClick={addTodo}>Add Todo</button>
    </>
 );
};

export default memo(Todos);

Try running this and clicking the count increment button.

The Todos component re-renders despite no changes to the todos because, due to “referential equality,” the addTodo function is recreated on each render, making it seem like it has changed.