避免使用 `useCallback` 重新渲染

时间:2021-02-21 19:22:51

标签: javascript reactjs react-hooks

我想弄清楚为什么当我点击一个特定的组件时,它的兄弟组件也会渲染

function CountButton({increment, count, number}) {
  console.log(`Render CountButton ${number}`)

  return <button onClick={() => increment(count + 1)}>{count}</button>
}

function DualCounter() {
  const [count1, setCount1] = React.useState(0)
  const increment1 = React.useCallback(() => setCount1(c => c + 1), [])
  const [count2, setCount2] = React.useState(0)
  const increment2 = React.useCallback(() => setCount2(c => c + 1), [])
  console.log('Render DualCounter')

  return (
    <>
      <CountButton count={count1} increment={increment1} number={1} />
      <CountButton count={count2} increment={increment2} number={2} />
    </>
  )
}

我使用 useCallback 并传递这些函数以避免在任何渲染中函数引用将是不同的引用。

1 个答案:

答案 0 :(得分:3)

您在兄弟组件 <CountButton /> 上看到了重新渲染,因为每次点击按钮更新计数器时,您实际上是在更新父组件 <DualCounter /> 中的状态值,即也会导致在该组件上重新渲染。

并且由于 DualCounter 被重新渲染,子组件也将重新渲染,在这种情况下包括 <CountButton /> 元素。

防止这种情况的解决方案是使用 React.memo() 包装 CountButton 组件。这将防止对 props 值没有任何更改的组件进行重新渲染。

示例如下:

function CountButton({increment, count, number}) {
  console.log(`Render CountButton ${number}`)

  return <button onClick={() => increment(count + 1)}>{count}</button>
}

const CountButtonMemo = React.memo(CountButton)

function DualCounter() {
  const [count1, setCount1] = React.useState(0)
  const increment1 = React.useCallback(() => setCount1(c => c + 1), [])
  const [count2, setCount2] = React.useState(0)
  const increment2 = React.useCallback(() => setCount2(c => c + 1), [])
  console.log('Render DualCounter')

  return (
    <>
      <CountButtonMemo count={count1} increment={increment1} number={1} />
      <CountButtonMemo count={count2} increment={increment2} number={2} />
    </>
  )

另一种解决方案是不对由 DualCounter 组件上的事件引起的每次更改更新 CountButton 状态,这将停止在其兄弟组件上触发不需要的重新渲染。如果这对您的应用有意义,您可以直接在每个 CountButton 组件上处理状态。

或者,您可以使用 React 状态管理工具,例如 Redux,它也可以解决这个问题,负责将应用的状态与组件本身分离。