Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
496 views
in Technique[技术] by (71.8m points)

javascript - When to use useEffect without dependencies vs. direct assignment?

I am writing a React hook that allows me to use setInterval in my components. In order to do so I need to keep the latest version of the callback in a ref so that it can be accessed from the scope of the interval later on.

This is my code so far:

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])
}

My question is about the first instance of useEffect where the latest value is passed to the ref. According the the React documentation this code will execute after my component has rendered.

I can imagine this is useful when you are passing a ref to an element so you can be sure that it has a value after it has rendered. But if my code doesn't care about when the component renders, does it still make sense to keep this in a useEffect?

Would it make sense that I rewrite the code as follows:

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])
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

When to use useEffect without dependencies vs. direct assignment?

From docs:

The Effect Hook lets you perform side effects in function components

  • A useEffect without dependencies (or undefined as dependencies) will be run at first render and every subsequent re-render, But always as a side-effect i.e. after the component has rendered.
  • A direct assignment (a sync operation) will be run at first render and every subsequent re-render, But always as in the render cycle. It may affect performance or delay the rendering.

So, when to use which one? It depends on your use case.

Which one to use in this (in question) use case?

I would say neither

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

nor

callbackRef.current = callback

seems correct in this use case.

Because we don't want to do the assignment - callbackRef.current = callback at every re-render. But we want to do it when there is a change in callback. So, the below one seems better:

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

You may see this blog and this related post.


A demo which shows that an effect runs after as a side-effect (Log inside effect is always the last):

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>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...