-2

Can anyone explain why setId(120) is being ignored?

The console output is:

enter image description here

import { useState, useEffect } from 'react';
import './App.css';

function App() {
    const [id, setId] = useState(0);

    useEffect(() => {
        console.log('before');
        setId(120);
        console.log('after');
    }, []);

    useEffect(() => {
        console.log('id', id);
        setId(555);
    }, [id]);

    return (
        <div className="App">
            <div>id=[{id}]</div>
        </div>
    );
}

export default App;
Edward Tanguay
  • 189,012
  • 314
  • 712
  • 1,047
  • 2
    You're seeing _batching_ in action; if you use e.g. `setId(previousId => (console.log("previousId", previousId), 555));` you can see that it _does_ get `120`, it's not completely ignored. You're closed over `id = 0` until the component renders again. – jonrsharpe Apr 08 '22 at 13:12
  • 1
    There’s a first-render effect that sets`id` and an effect dependent on`id` that also sets `id`. End result is you may never see `120` in a meaningful way since it’s overwritten. Meaningless state changes may be rolled up. – Dave Newton Apr 08 '22 at 13:13
  • @GiorgiMoniava Because it’s a dupe? – Dave Newton Apr 08 '22 at 13:21
  • @GiorgiMoniava It technically is. It's a result of logging the value on the same render as an update and expecting it to be the updated value. However, there's the extra element here of two `useEffect`s which may be the real source of confusion. I've found that lots of people don't understand that a `useEffect` with dependencies also runs after mounting. The OP may be thinking that their console log is running on a subsequent render, not the first.. Not sure. – Brian Thompson Apr 08 '22 at 13:24
  • @BrianThompson I mean maybe he was expecting that 120 gets printed on second render, but it doesn't even on second render (because it is overwritten apparently by 555) – Giorgi Moniava Apr 08 '22 at 13:25
  • The confusing aspect is when I remove `setId(555)`, it sends 120 to the console. – Edward Tanguay Apr 08 '22 at 13:27
  • @GiorgiMoniava I don't think anyone is arguing against that point. It *is* getting overwritten by `setId(555)`. 1st render - setId(120), console.log(id), setd(555) --- current value is 0 (initial) . 2nd render - (useEffect is triggered again since `id` changed) console.log(id) --- current value is 555 since it was the *last* value passed to the state updater. – Brian Thompson Apr 08 '22 at 13:29
  • @BrianThompson Yeah that's why I thought there is no duplicate for that – Giorgi Moniava Apr 08 '22 at 13:30
  • Well if we agree on the order of what just happened, then an question of why `console.log(id)` never yields `120` is because `id` is still the closed over value of 0, and not the *new* value of 120. So the answer would be state updates don't happen immediately. – Brian Thompson Apr 08 '22 at 13:32
  • @EdwardTanguay *We* don’t control (roughly) *when* `useEffect` functions run, only their dependencies. State resolution is (roughly) a black box. Self-referential effects can easily get rolled up into a single state update. Noting that recursive effects are Generally Bad; here it’s a primitive so we get away with it. – Dave Newton Apr 08 '22 at 13:34
  • @BrianThompson it seems I mistakenly also voted :). I still think this is not dupe because "updates don't happen immediately" **isn't only factor here**. But anyway, no point in debating this too much, maybe I see it differently :) – Giorgi Moniava Apr 08 '22 at 13:39
  • 1
    https://reactjs.org/docs/hooks-reference.html#:~:text=batching%20of%20state%20updates – Dave Newton Apr 08 '22 at 13:51

1 Answers1

0

When you saying "ignored" I guess you meaning "Why 120 isn't logged?".

You are logging the closure value of id.

Notice that useEffect with [id] as dep array will be called on mount too (similar as with [] an empty dep array).

Read more at useEffect use cases

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118