НАЙКРАЩИЙ САЙТ ДЛЯ ВЕБ-РОЗРОБНИКІВ
React. W3Schools українською. Уроки для початківців

En

React хук useCallback


Хук React useCallback повертає мемоізовану функцію зворотного виклику.

Подумайте про запам’ятовування як про кешування значення, щоб його не потрібно було перераховувати.

Це дозволяє нам ізолювати ресурсомісткі функції, щоб вони не запускалися автоматично при кожному рендері.

Хук useCallback запускається лише під час оновлення однієї з його залежностей.

Це може покращити продуктивність.

Хуки useCallback і useMemo схожі. Основна відмінність полягає в тому, що useMemo повертає мемоізоване значення, а useCallback повертає мемоізовану функцію. Ви можете дізнатися більше про useMemo в розділі useMemo.


Проблема

Однією з причин використання useCallback є запобігання повторному рендерингу компонента, якщо його атрибути не змінено.

У цьому прикладі ви можете подумати, що компонент Todos не відобразиться повторно, якщо todos змінити:

Це приклад, схожий на приклад у розділі React.memo.

Приклад:

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

Виконати приклад »

Спробуйте запустити це та натисніть кнопку збільшення кількості.

Ви помітите, що компонент Todos повторно відображається, навіть якщо todos не змінюються.

Чому це не працює? Ми використовуємо memo, тому компонент Todos не повинен повторно відтворюватися, оскільки ані стан todos, ані функція addTodo не змінюються, коли кількість збільшується.

Це через те, що називається "referential equality" ("референційна рівність").

Кожного разу, коли компонент повторно рендериться, його функції створюються заново. Через це функція addTodo фактично змінилася.


Рішення

Щоб виправити це, ми можемо використати хук useCallback, щоб запобігти повторному створенню функції без необхідності.

Використовуйте хук useCallback, щоб запобігти непотрібному повторному рендерингу компонента Todos:

Приклад:

index.js

import { useState, useCallback } 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 = useCallback(() => {
setTodos((t) => [...t, "New Todo"]);
}, [todos]);

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

Виконати приклад »

Тепер компонент Todos буде повторно відображатися лише тоді, коли змінюється властивість todos.