Реагировать — Это хорошее использование крючка обратного вызова?

#reactjs #react-hooks

Вопрос:

Я пытаюсь понять обоснование того, что кажется новой тенденцией определения всех обратных вызовов и/или обработчиков событий с useCallback помощью крючка.

Насколько я понимаю, это onClick === Callback_A правда до тех пор, пока x остается прежним:

 const onClick = useCallback(() => 42, [x]);
//                          ^^^^^^^^
//                          Callback_A
 

Однако выражение функции (1-й аргумент useCallback ) всегда будет новым после каждого рендеринга. Он отбрасывается только в том случае, если x он не изменился.

Предположим, что компонент по какой-то причине отображается три раза, но x остается неизменным на протяжении всего:

 /*********************************** 1st render ***/

const onClick = useCallback(() => 42, [x]);
//    ^^^^^^^               ^^^^^^^^
//    Callback_A            Callback_A

/*********************************** 2nd render ***/

const onClick = useCallback(() => 42, [x]);
//    ^^^^^^^               ^^^^^^^^
//    Callback_A            Callback_A'

/*********************************** 3rd render ***/

const onClick = useCallback(() => 42, [x]);
//    ^^^^^^^               ^^^^^^^^
//    Callback_A            Callback_A''
 

Если новое выражение функции (т. Е. () => 42 ) создается при каждом рендеринге независимо от x того, является ли оно одинаковым или нет, почему бы просто не назначить его onClick напрямую?

Когда я делаю это замечание во время проверки кода, ответ всегда является вариантом «это-для того, чтобы избежать бесполезной повторной визуализации дочерних компонентов».

Я хотел проверить эту теорию на практике и создал простое приложение с пятью кнопками:
(Смотрите скринкаст ниже)

  • Каждая кнопка связана со своим собственным отдельным состоянием.
  • Приложение повторно отображает каждые две секунды, чтобы обновить значение только одной кнопки.
  • Все остальные кнопки всегда остаются неизменными
  • Каждой кнопке присваивается свой собственный обработчик «onclick».
  • Каждый обработчик «onclick»» генерируется » по-разному (например, с useCallback помощью или без)

I count how many times onclick handlers are generated and how many onclick handlers each button actually uses:

 import { useCallback, useEffect, useState } from "react";

const stats =
  { N: {}
  , B: {}
  , C: {}
  , D: {}
  , E: {} };

const register_handler = btn => {
  const now = performance.now();
  stats[btn][now] = false;
  return () => stats[btn][now] = true;
};

const print_stats = () => {
  console.table(
    Object.entries(stats).map(([button, handlers]) => ({
      button,
      count_generated: Object.keys(handlers).length,
      count_executed: Object.values(handlers).filter(b => b).length
    }))
  );
};

const e_click = register_handler('E');

export default function App() {
  // Only `n` will change. All other vars will remain the same.
  const [n, write_n] = useState(1);
  const [b] = useState('B');
  const [c] = useState('C');
  const [d] = useState('D');
  const [e] = useState('E');

  // Forces <App/> to re-render every two seconds.
  useEffect(() => setTimeout(() => write_n(n   1), 2000));

  const n_click = useCallback(register_handler('N'), [n]);
  const b_click = useCallback(register_handler('B'), [b]);
  const c_click = register_handler('C');
  
  return (
    <fieldset>
      <legend>your buttons are belong to us</legend>
      <button value={n} onClick={n_click}>{n}</button>
      <button value={b} onClick={b_click}>{b}</button>
      <button value={c} onClick={c_click}>{c}</button>
      <button value={d} onClick={register_handler('D')}>{d}</button>
      <button value={e} onClick={e_click}>{e}</button>
      <hr />
      <button onClick={print_stats}>print stats</button>
    </fieldset>
  );
}
 

I click on each button exactly three times then print some stats:

Button Number of handlers generated Number of handlers used
N 24 3
B 24 1
C 24 3
D 24 3
E 1 1

Наблюдения:

  1. За исключением кнопки E , все остальные кнопки создали слишком много обработчиков событий.
  2. Обе кнопки C и D еще не использовались useCallback , они не перерисовывались.

За исключением кнопки E , все остальные кнопки генерировали одинаковое количество обработчиков событий, но потребляли всего несколько. Некоторые повторно использовали один и тот же обработчик событий, в то время как другие каждый раз использовали новый. Насколько мы уверены в том, что польза от кэширования с useCallback перевешивает его стоимость? Я полагаю, что все, что связано с кешем, имеет некоторые компромиссы.

Эта безусловная любовь к useCallback также сопряжена с некоторыми нетривиальными затратами на техническое обслуживание. Не все знакомы с ним, и я подозреваю, что его использование в какой-то степени обусловлено программированием культа карго.

В этом конкретном примере было бы оправданно использовать useCallback кого-либо и почему?


https://codesandbox.io/s/experimenting-with-usecallback-c4jf1

введите описание изображения здесь

Комментарии:

1. Начиная с «Я пытаюсь понять обоснование того, что кажется новой тенденцией определения всех обратных вызовов и/или обработчиков событий с помощью крючка useCallback». Как это тенденция? royi-codes.vercel.app/тысячи обратных вызовов, звучит как преждевременная оптимизация, ее, безусловно, не следует злоупотреблять

2. useCallback это просто еще одна форма запоминания, о которой существует множество предупреждений о чрезмерном использовании. Чрезмерное использование-ошибка новичка, и распространенный аргумент против дополнительных повторных отправлений показывает, что они на самом деле не понимают последствий для производительности кода, который они пишут. Короче говоря, я думаю, что вы правы, что это неоправданно.