对使用React挂钩找到的解决方案的性能的担忧

时间:2019-02-13 16:25:15

标签: javascript reactjs react-hooks

我想表达我对钩子的担忧。

出于学习目的,使用钩子重构/重写React库(react-numpad)的过程中,我一直坚持在关键事件上使用eventListener实现。

使用类,只需在componentDidMount和componentWillUnmount生命周期内添加addListener即可实现。

useKeyPress挂钩

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

挂钩使用情况

  

键盘组件是接收值作为道具并调用的子级   更新keydown作为回调。如果useEffect(在挂钩内)具有空数组,则不会更新此值

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

现在在每个渲染器上添加和删除addListener。这不是以前发生的,只有在安装或卸载组件时才发生。

使用钩子与使用类有相同的行为吗?

1 个答案:

答案 0 :(得分:1)

您不需要在每个渲染器中添加或删除事件侦听器,并且完全可以像以前一样添加一次事件侦听器,方法是使用useEffect并将空数组用作第二个参数。像这样:

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

问题

这样做的问题是,由于该函数仅运行一次,因此更改后无法更新其可访问的值(例如props或其他外部作用域变量)。

例如:

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

即使调用setFoo之后,此代码也将始终显示1,因为该函数已绑定到foo的旧值。

推荐的解决方案

当您只需要根据自身和其他值继续更新某些状态时,最好的选择是使用带有useReducer的reducer。您可以从useEffect函数(或任何其他位置)使用dispatch,然后将以已更新状态调用reducer,从而允许其读取并根据需要对其进行更新。只需传递有关动作本身的任何新信息即可。

在您使用的情况下,您只需调度相关的事件信息,并让化简处理更新。

其他选项

反应事件

如果您可以使用标准的react事件而不是任何addEventListener,则不必考虑这一点,因为该函数将在每个渲染器上更新,而无需您执行任何操作。您可以将函数作为道具传递,以便它们也可以从子组件中使用。

参考

您可以改用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用作值,甚至可以将其用于要从事件处理程序调用的函数。这不是更干净的路径,但是您可以使其正常工作。