【问题标题】:Concerns about performance with the solution found using React hooks对使用 React 钩子找到的解决方案的性能的担忧
【发布时间】:2019-07-07 13:39:10
【问题描述】:

我想公开我对钩子的担忧。

在使用钩子重构/重写反应库 (react-numpad) 以进行学习时,我一直坚持在关键事件上实现 eventListener。

使用类,只需在 componentDidMount 和 componentWillUnmount 生命周期中添加 addListener 即可完成。

使用KeyPress挂钩

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

挂钩用法

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

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

现在在每次渲染时添加和删除 addListener。这以前没有发生,但仅在安装或卸载组件时发生。

钩子的行为是否与使用类的行为相同?

【问题讨论】:

  • 我不确定你在问什么。您是正确的,默认情况下每个渲染都会发生效果,并且您可以使用数组作为第二个参数来限制重新计算发生的时间是正确的。
  • @NicholasTower 将数组作为第二个参数,该解决方案不再有效。我想知道在这种情况下使用钩子是否会降低性能。
  • 我不明白为什么你觉得你需要在每次渲染时添加或删除监听器。使用 useEffect,您可以在挂载时添加一次,并在卸载时将其删除,就像您之前所做的那样,通过将空数组作为值传递。如果您需要更频繁地运行另一种效果,则可以有多个 useEffect。
  • @Anxo,如果你仔细阅读问题描述,当我使用 useEffect 和空数组时,prop 值是不可访问的。
  • 我想我现在已经理解你了。您绝对可以使用空数组从 useEffect 访问道具,但该函数不会在每次渲染时更新,因此不会更新道具。你只需要解决这个事实。我会添加一个答案。

标签: javascript reactjs react-hooks


【解决方案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)

此代码将始终打印 1,即使在调用 setFoo 之后也是如此,因为该函数绑定到 foo 的旧值。

推荐解决方案

当您只需要根据自身和其他值不断更新某些状态时,最好的选择是使用带有 useReducer 的 reducer。您可以从 useEffect 函数(或任何其他地方)使用 dispatch,reducer 将使用更新的状态调用,允许它读取它并根据需要更新它。只需传递有关操作本身的任何新信息。

根据您的使用情况,您只需发送相关的事件信息,并让 reducer 处理更新。

其他选项

反应事件

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

参考

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

【讨论】:

  • 伙计,这不是 React hackish... 因此“Happy hacking!”。
猜你喜欢
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
  • 2020-11-02
  • 2021-07-08
相关资源
最近更新 更多