#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 |
Наблюдения:
- За исключением кнопки
E
, все остальные кнопки создали слишком много обработчиков событий. - Обе кнопки
C
иD
еще не использовалисьuseCallback
, они не перерисовывались.
За исключением кнопки E
, все остальные кнопки генерировали одинаковое количество обработчиков событий, но потребляли всего несколько. Некоторые повторно использовали один и тот же обработчик событий, в то время как другие каждый раз использовали новый. Насколько мы уверены в том, что польза от кэширования с useCallback
перевешивает его стоимость? Я полагаю, что все, что связано с кешем, имеет некоторые компромиссы.
Эта безусловная любовь к useCallback
также сопряжена с некоторыми нетривиальными затратами на техническое обслуживание. Не все знакомы с ним, и я подозреваю, что его использование в какой-то степени обусловлено программированием культа карго.
В этом конкретном примере было бы оправданно использовать useCallback
кого-либо и почему?
https://codesandbox.io/s/experimenting-with-usecallback-c4jf1
Комментарии:
1. Начиная с «Я пытаюсь понять обоснование того, что кажется новой тенденцией определения всех обратных вызовов и/или обработчиков событий с помощью крючка useCallback». Как это тенденция? royi-codes.vercel.app/тысячи обратных вызовов, звучит как преждевременная оптимизация, ее, безусловно, не следует злоупотреблять
2.
useCallback
это просто еще одна форма запоминания, о которой существует множество предупреждений о чрезмерном использовании. Чрезмерное использование-ошибка новичка, и распространенный аргумент против дополнительных повторных отправлений показывает, что они на самом деле не понимают последствий для производительности кода, который они пишут. Короче говоря, я думаю, что вы правы, что это неоправданно.