何时使用无依赖关系的 useEffect 与直接赋值?

IT技术 javascript reactjs react-hooks
2021-05-13 22:18:05

我正在编写一个 React 钩子,它允许我setInterval在我的组件中使用为此,我需要在 ref 中保留最新版本的回调,以便稍后可以从间隔范围访问它。

到目前为止,这是我的代码:

import { useRef, useEffect } from 'react'

export default function useInterval(
  callback: () => void,
  delay: number | null,
) {
  const callbackRef = useRef(callback)

  // Remember the latest callback.
  useEffect(() => {
    callbackRef.current = callback
  })

  useEffect(() => {
    // Don't schedule if no delay is specified.
    if (delay === null) {
      return
    }

    const id = setInterval(() => callbackRef.current(), delay)

    return () => clearInterval(id)
  }, [delay])
}

我的问题是关于将useEffect最新值传递给 ref的第一个实例根据 React 文档,此代码将在我的组件呈现后执行。

我可以想象这在您将 ref 传递给元素时很有用,因此您可以确保它在呈现后具有值。但是,如果我的代码不关心组件何时呈现,那么将其保留在useEffect.

我将代码重写如下是否有意义:

import { useRef, useEffect } from 'react'

export default function useInterval(
  callback: () => void,
  delay: number | null,
) {
  const callbackRef = useRef(callback)

  // Remember the latest callback.
  callbackRef.current = callback

  useEffect(() => {
    // Don't schedule if no delay is specified.
    if (delay === null) {
      return
    }

    const id = setInterval(() => callbackRef.current(), delay)

    return () => clearInterval(id)
  }, [delay])
}
1个回答

什么时候使用useEffect无依赖 vs. 直接赋值?

从文档:

效果挂钩,您可以执行的功能组件的副作用

  • useEffect而不依赖(或undefined作为依赖性)将在运行第一渲染和随后重新呈现,总是作为一个副作用即组件已呈现。
  • 直接分配(同步操作)将在第一次渲染和每次后续重新渲染时运行,但始终与渲染周期一样。它可能会影响性能或延迟渲染。

那么,什么时候使用哪一种呢?这取决于您的用例。

在这个(有问题的)用例中使用哪一个?

我会说没有

useEffect(() => {
  callbackRef.current = callback
})

也不

callbackRef.current = callback

在这个用例中似乎是正确的。

因为我们不想callbackRef.current = callback在每次重新渲染时进行分配但是我们希望在callback. 所以,下面的似乎更好

useEffect(() => {
  callbackRef.current = callback
}, [callback])

您可能会看到此博客此相关帖子


显示效果作为副作用运行的演示(登录内部效果始终是最后一个):

function useInterval(callback, delay) {
  const callbackRef = React.useRef(callback)

  React.useEffect(() => {
    callbackRef.current = callback
  }, [callback])

  React.useEffect(() => {
    if (delay !== null) {
      const id = setInterval(() => callbackRef.current(), delay)
      return () => clearInterval(id)
    }
  }, [delay])
}

function Demo() {
  const [count, setCount] = React.useState(0)

  function doThis() {
    setCount(count + 1)
  }

  useInterval(doThis, 1000)

  console.log('log - before effect')
  React.useEffect(() => {
    console.log('log inside effect')
  })
  console.log('log - after effect')

  return <h1>{count}</h1>
}

ReactDOM.render(<Demo />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<body>
<div id="root"></div>
</body>