Опасения по поводу производительности решения, найденного с использованием React-хуков - PullRequest
0 голосов
/ 13 февраля 2019

Я хотел бы высказать озабоченность, касающуюся хуков.

При рефакторинге / переписывании библиотеки реагирования ( response-numpad ) с использованием хуков для целей обучения я застрял среализация для eventListener для ключевого события.

Для класса это просто достигается добавлением addListener в жизненные циклы componentDidMount и componentWillUnmount.

useKeyPress hook

https://github.com/gpietro/react-numpad/blob/issue/8/numpad-visible-by-default/lib/hooks/useKeyPress.js

обновление обратного вызова

Компонент NumPad является родительским с состоянием значения

https://github.com/gpietro/react-numpad/blob/issue/8/numpad-visible-by-default/lib/components/NumPad.js#L93

использование перехвата

Компонент клавиатуры - это дочерний элемент, принимающий значение в качестве реквизита, и вызывающий обновление по нажатию клавиши в качестве обратного вызова.Это значение не обновляется, если useEffect (внутри хука) имеет пустой массив

https://github.com/gpietro/react-numpad/blob/issue/8/numpad-visible-by-default/lib/elements/KeyPad.js#L70

Теперь при каждом рендере добавляется и удаляется addListener.Этого не было раньше, но только при установке или демонтаже компонента.

Существует ли такое же поведение с крючками, как при использовании класса?

1 Ответ

0 голосов
/ 15 февраля 2019

Вам не нужно добавлять или удалять прослушиватели событий в каждом рендере, и добавление их только один раз, как и прежде, вполне возможно, если использовать useEffect с пустым массивом в качестве второго аргумента.Что-то вроде:

useEffect(() => {
  // your didMount code here
  return () => {
    // your willUnmount code here
 }
}, [])

Проблема

Ваша проблема заключается в том, что, поскольку функция запускается только один раз, значения, доступные из нее (такие как реквизиты или другие внешние области видимости)переменные) не будут обновляться при их изменении.

Например:

const [foo, setFoo] = setState(1)
useEffect(() => {
  const id = window.setInterval(() => console.log(foo), 1000)
  return () => window.clearInterval(id)
}, [])
// ...
setFoo(2)

Этот код всегда будет печатать 1, даже после вызова setFoo, поскольку функция привязана к старомузначение foo.

Рекомендованное решение

Когда вам нужно только обновлять какое-то состояние на основе самого себя и другого значения, лучшим вариантом будет использование редуктора с useReducer.Вы можете использовать диспетчеризацию из функции useEffect (или из любого другого места), и редуктор будет вызываться с обновленным состоянием, что позволяет ему читать его и обновлять по мере необходимости.Просто передайте любую новую информацию о самом действии.

В случае использования вы просто отправите соответствующую информацию о событии и получите редуктор для обработки обновлений.

Другие варианты

События реагирования

Если вы можете использовать стандартные события реакции вместо какого-либо addEventListener, вам не придется об этом думать, потому что функция будет обновляться при каждом рендеринге, без каких-либо действий со стороны вашегочасть.Вы можете передавать функции как реквизиты, чтобы они тоже использовались от дочернего компонента.

Refs

Вместо этого вы можете использовать ref, потому что они всегда возвращают один и тот же объект, изменяя его вместо созданияновый.Это позволяет любой функции, имеющей доступ к ref, прочитать текущее значение.

Пример:

const fooRef = useRef(1)
fooRef.current = foo
useEffect(() => {
  const id = window.setInterval(() => console.log(fooRef.current), 1000)
  return () => window.clearInterval(id)
}, [])
// ...
fooRef.current = 2

Вы можете использовать ref для значений или даже использовать его для функции, котораяВы бы позвонили из обработчика событий.Это не чистый путь, но вы можете заставить его работать.

...