В типичном компоненте React на основе классов я бы создал обработчик событий следующим образом:
class MyComponent extends Component {
handleClick = () => {
...
}
render() {
return <button onClick = {this.handleClick}>Click Me</button>;
}
}
Однако у меня есть два варианта, когда я использую функциональную парадигму на основе хуков:
const MyComponent = () => {
const [handleClick] = useState(() => () => {
...
});
return <button onClick = {handleClick}>Click Me</button>;
};
или альтернативно:
const MyComponent = () => {
const handleClick = useRef(() => {
...
});
return <button onClick = {handleClick.current}>Click Me</button>;
};
Какой из них объективно лучше и по какой причине? Есть ли другой (лучший) способ, о котором я еще не слышал и не открыл?
Спасибо за помощь.
Обновлено: я привел пример здесь, в CodeSandbox, показывающий оба метода. Кажется, ни один из них не воссоздает обработчик событий при каждом рендеринге без необходимости, как вы можете видеть из приведенного там кода, поэтому я думаю, что о возможной проблеме с производительностью не может быть и речи.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Не рекомендую ни useState, ни useRef.
Здесь вообще не нужен крючок. Во многих случаях я бы рекомендовал просто сделать это:
const MyComponent = () => {
const handleClick = (e) => {
//...
}
return <button onClick = {handleClick}>Click Me</button>;
};
Однако иногда рекомендуется избегать объявления функций внутри функции рендеринга (например, правило jsx-no-lambda tslint). На это есть две причины:
Я бы не стал сильно беспокоиться о первом пункте: хуки будут объявлять функции внутри функций, и маловероятно, что эти затраты станут основным фактором производительности ваших приложений.
Но второй момент иногда имеет место: если компонент оптимизирован (например, с помощью React.memo или определен как PureComponent) так, что он повторно рендерится только при предоставлении новых реквизитов, передача нового экземпляра функции может привести к повторному рендерингу компонента. без необходимости.
Чтобы справиться с этим, React предоставляет хук useCallback для запоминания обратных вызовов:
const MyComponent = () => {
const handleClick = useCallback((e) => {
//...
}, [/* deps */])
return <OptimizedButtonComponent onClick = {handleClick}>Click Me</button>;
};
useCallback будет возвращать новую функцию только при необходимости (каждый раз, когда значение в массиве deps изменяется), поэтому OptimizedButtonComponent не будет повторно отображать больше, чем необходимо. Таким образом, это решает проблему № 2. (Обратите внимание, что это не решает проблему № 1, каждый раз, когда мы визуализируем, новая функция все еще создается и передается useCallback)
Но я бы сделал это только там, где это необходимо. Вы можете обернуть каждый обратный вызов в useCallback, и это сработает... но в большинстве случаев это ничего не поможет: ваш исходный пример с <button> не выиграет от запомненного обратного вызова, поскольку <button> не является оптимизированным компонентом.
your original example with <button> won't benefit from a memoized callback, since <button> isn't an optimized component. Что вы имеете в виду здесь? Почему это не помогает? Свойство DOM onClick просто не будет бездумно обновляться при каждом рендеринге, разве это не то, для чего нам это нужно?
@KostiantynKolesnichenko Основной смысл этих оптимизаций заключается в том, чтобы избежать затрат на логику компонента оказывать, и нет функции рендеринга для элемента DOM - по сути, он просто создает статический объект, представляющий элемент. React потребуется обновить DOM, но затраты на переключение обработчика событий почти наверняка тривиальны. Но стоимость повторного рендеринга всего нового компонента из-за изменения экземпляра функции может быть ненужной.
Действительно ли согласователь затрагивает DOM, когда заменяет обработчики событий?
@thorn̈ Да, я считаю, что нет; React использует синтетические события и делегирует их вашим пользовательским функциям-обработчикам.
Вам не нужно использовать хук или классические компоненты для создания функций внутри компонента. Вы по-прежнему можете объявить константную функцию либо внутри тела функционального компонента, либо снаружи.