Reaguj useCallbackhak


React useCallbackHook zwraca zapamiętaną funkcję zwrotną.

Pomyśl o zapamiętywaniu jako buforowaniu wartości, aby nie trzeba było jej ponownie obliczać.

To pozwala nam odizolować funkcje wymagające dużej ilości zasobów, aby nie uruchamiały się automatycznie przy każdym renderowaniu.

Hook działa tylko wtedy useCallback, gdy jedna z jego zależności zostanie zaktualizowana.

Może to poprawić wydajność.

The useCallbacki useMemoHooki są podobne. Główna różnica polega na tym, że useMemozwraca zapamiętaną wartość i useCallbackzwraca zapamiętaną funkcję . Więcej informacji o useMemo znajdziesz w rozdziale useMemo .


Problem

Jednym z powodów użycia useCallbackjest zapobieganie ponownemu renderowaniu komponentu, chyba że jego właściwości uległy zmianie.

W tym przykładzie możesz pomyśleć, że Todosskładnik nie zostanie ponownie wyrenderowany, chyba że todoszmiana:

Jest to podobny przykład do tego w sekcji React.memo .

Przykład:

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

Spróbuj uruchomić to i kliknij przycisk zwiększania liczby.

Zauważysz, że Todosskładnik jest ponownie renderowany, nawet jeśli todosnie ulegną zmianie.

Dlaczego to nie działa? Używamy memo, więc Todoskomponent nie powinien być ponownie renderowany, ponieważ ani todosstan, ani addTodofunkcja nie zmieniają się, gdy liczba jest zwiększana.

Dzieje się tak z powodu czegoś, co nazywa się „równością referencyjną”.

Za każdym razem, gdy komponent jest ponownie renderowany, jego funkcje są odtwarzane. Z tego powodu addTodofunkcja faktycznie się zmieniła.


w3schools CERTIFIED . 2022

Zostać certyfikowanym!

Uzupełnij moduły React, wykonaj ćwiczenia, podejdź do egzaminu i uzyskaj certyfikat w3schools!!

95 $ ZAPISZ

Rozwiązanie

Aby to naprawić, możemy użyć useCallbackzaczepu, aby zapobiec ponownemu utworzeniu funkcji, chyba że jest to konieczne.

Użyj useCallbackzaczepu, aby zapobiec Todosniepotrzebnemu ponownemu renderowaniu komponentu:

Przykład:

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

Teraz Todoskomponent będzie ponownie renderowany tylko wtedy, gdy todoszmieni się właściwość.