1

Can someone explain why this even triggers the useEffect? The setState function in the useState hook always seems to be a function. Its not undefined (at initialization) at any point in time?

const [state, setState] = useState(0)
useEffect(() => {console.log('triggers')}, [setState])

I'm away of the caveats with React 18s mounting/unmounting, but since setState never changes the effect should not fire? What am I missing here?

The above code is basically the same as writing:

useEffect(() => {...}, [])
Michael Falck Wedelgård
  • 2,943
  • 1
  • 27
  • 39

2 Answers2

1

It will call on the initial render only

React will call the callback function of useEffect on the initial render and when any of the dependency changes from dependency array.

useEffect(() => {
    console.log("triggers");
  }, [setState]);

CODESANDBOX DEMO

So, The callback function will trigger on the inital render and when the setState changes (which will never change)

DecPK
  • 24,537
  • 6
  • 26
  • 42
0

Long story short, useEffect will always run when the component first loads, even if you have a dependency list,

useEffect(()=> { console.log("hello") }) // will log "hello"
useEffect(()=> { console.log("hello") } []) // will log "hello"
useEffect(()=> { console.log("hello") } [x]) // will log "hello"
useEffect(()=> { console.log("hello") } [x, y, z]) // will log "hello"

if you only want your useEffect to fire if the dependency element in the dependency list changes, you need to set some logic inside, for instance, for your case:

const [state, setState] = useState(0)
useEffect(() => {
  if(!state) return
  console.log('triggers')
}, [state])

and the same if you have something else to check, for instence if you have the initial state set to an empty array, you would do the condition like if(!state.length) return, and so on.

This is kinda sucks when you have multiple elements in the dependency list, that's why the react developers recommend having mutliple useEffects in your component for such case, where each useEffect() handles a piece of logic.

Normal
  • 1,616
  • 15
  • 39
  • While the reasoning is correct, I don't believe that workaround would actually prevent it running on first render (At the point where the effect runs, the dependency has already updated, and a setState function would be truthy) – DBS Oct 05 '22 at 08:58
  • @DBS, yep sorry for that, I thought `setState` was undefined, the OP question is not correct, nobody checks to see if `setState` is truthy or not, people use the `state` instead in the dependency array, so I'll update my answer and let's hope the OP is going to update his question as well. – Normal Oct 05 '22 at 09:02
  • Just FYI, I think the first sentence is the only part of this answer that is relevant to the question OP has asked about. The `React.StrictMode` component is mounting the component twice, so you'll see ***two*** mounting `useEffect` effects. The rest of your answer is only relevant to the `useEffect` hook on subsequent render cycles *after* mounting. – Drew Reese Oct 05 '22 at 09:34