React Hooks:重新渲染时的setState功能

时间:2019-06-13 14:58:49

标签: reactjs react-hooks

这是一个有关使用钩子时可能导致性能下降的问题。 引用React文档中的function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </> ); } 示例:

useState
  

React保证setState函数身份是稳定的,并且在重新渲染时不会更改。这就是为什么可以安全地从useEffect或useCallback依赖项列表中省略的原因。

我有两个关于identity is stable and won’t change on re-renders用法的查询:

  • anonymous是什么意思?
  • 我可以看到,每个按钮都有一个setState identity is stable函数作为事件处理程序传递。即使React声称的useCallback是正确的,难道每次重新渲染都不会重新创建匿名函数吗?

如果使用{{1}}来定义已记忆的函数并将其用作事件处理程序,效率会更高吗?

3 个答案:

答案 0 :(得分:1)

将此组件视为问题1的说明。

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);

  // after each render we record the value of setCount
  const ref = useRef(null);
  useEffect(() => {
    ref.current = setCount;
  }, [setCount]);

  return (
    <>
      <div>
        Did setCount change from since render?{" "}
        {(!Object.is(ref.current, setCount)).toString()}
      </div>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}
  1. 这意味着在不同的组件渲染期间,useState将相同(Object.is)

  2. 是的,匿名函数将在每个渲染器上重新创建

  

如果将useCallback用于定义记忆的函数并将其用作事件处理程序,效率会更高吗?

在这种情况下,不可以,因为useCallback并不是免费提供的,而无论如何按钮都会被渲染。但是当我们有一个非常重的组件来呈现useCallback时,它将阻止它不必要地重新渲染

答案 1 :(得分:1)

  1. 什么是身份稳定的,并且在重新渲染后不会改变?
  

您的setCount方法是稳定的,并且在重新渲染时不会更改。

  1. 我可以看到,每个按钮都有一个匿名函数作为事件处理程序传递。即使React声称的setState身份是稳定的,也不会在每次重新渲染时重新创建匿名函数吗?
  

您可以,但是这里不需要这样做,因为您没有将其传递给子组件,如果要将其传递给另一个React组件,则应该使用useCallback。

答案 2 :(得分:1)

  

什么是身份稳定的,并且在重新渲染后不会改变?

useState返回的函数在整个渲染周期内都不会改变。也就是说,在第一个渲染周期设置状态后,仍可以调用在第一个渲染周期返回的调度函数。这样,您可以在任何时候使用分派功能来设置钩子,而无需在函数引用更改时刷新钩子。

  useEffect(() => {
    setCount(initialCount);
  }, [ initialCount, setCount ]); // <--- setCount is not needed here
  

我可以看到,每个按钮都有一个匿名函数作为事件处理程序传递。即使React声称的setState身份是稳定的,也不会在每次重新渲染时重新创建匿名函数吗?

是的,确实会在重新渲染时重建箭头功能。另一种方法是使用useCallback来记住一个回调,但是要花多少钱呢?调用useCallback的成本,记忆该回调的成本,构建对该回调的引用的成本以及在每次重新渲染时都检索该回调的成本远远超过了在每次重新渲染时仅构建一个函数的好处。

useCallback函数本身是20 lines long,并带有对其他内部React API的其他3个嵌套函数调用。所有这些都可以防止在每个渲染器上执行单行功能?数学的总和并不等于useCallback。唯一有用的情况是,您希望通过引用或其他某种方式使回调具有“稳定的身份”,以便您可以将回调作为道具传递而不会引起过多的重新渲染。