React useCallback Hook


React useCallback Hook returns a memorization function.


Think of memorization as keeping the value so that it does not need to be recalculated.


This allows us to separate the in-depth functions of the app so that it does not work automatically across all offers.

The useCallback Hook only works when one dependent is updated.

This can improve performance.


useCallback and useMemo Hooks are the same. The main difference is that useMemo returns a memorized value and useCallback returns a memorized function. You can learn more about using useMemo in the Memo chapter.



One reason to use useCallback is to prevent the component from being re-rendered unless its resources are changed.

In this example, you might think that part of Todos will never give up again unless todos changes:


This is the same example as in the React.memo section.



Example

index.js

import { useState } from "react";
            import ReactDOM from "react-dom";
            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>
                </>
              );
            };
            
ReactDOM.render(<App />, document.getElementById('root'));
            

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 using this and click the calculate button.

You will notice that the Todos section also offers even when the todos do not change.

Why does this not work? We use memo, so part of Todos should not re-render as todos status or addTodo function changes when the number goes up.

This is due to something called "referential equality".

Each time a part donates, its functions are rebuilt. As a result, the function of addTodo has really changed.


Solution

To fix this, we may use the useCallback hook to prevent the work from being redone unless necessary.

Use the useCallback Hook to prevent part of Todos from redundant:


Example

index.js

import { useState, useCallback } from "react";
            import ReactDOM from "react-dom";
            import Todos from "./Todos";
            
            const App = () => {
              const [count, setCount] = useState(0);
              const [todos, setTodos] = useState([]);
            
              const increment = () => {
                setCount((c) => c + 1);
              };
              const addTodo = useCallback(() => {
                setTodos((t) => [...t, "New Todo"]);
              }, [todos]);
            
              return (
                <>
                  <Todos todos={todos} addTodo={addTodo} />
                  <hr />
                  <div>
                    Count: {count}
                    <button onClick={increment}>+</button>
                  </div>
                </>
              );
            };
            
ReactDOM.render(<App />, document.getElementById('root'));
            

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);
            

Now part of Todos will re-offer only when todos prop changes.