91528


useEffectの挙動について質問させていただきます。  
console.logで出力する値が遅れて出力されるのはなぜか というのが主旨になります。

[code]

    import { useEffect, useState } from "react";
    
    type inputSetting = {
        player : number;
        wolves : number;
        civil : number;
        timer : number;
    }
    
    export default function GameSetting() {
    
        const initialSetting = {
            player: 3,
            wolves: 1,
            civil: 2,
            timer: 2
        }
    
        const [property, setProperty] = useState<inputSetting>(initialSetting);
    
        const decrementPlayer = () => {
             setProperty({ ...property, player: property.player - 1 });
        }
        const incrementPlayer = () => {
             setProperty({ ...property, player: property.player + 1 });
        }
    
        // U1
        useEffect(() => {
            setProperty({...property, civil: property.player - property.wolves});
        },[property.player]);
        
        // U2
        useEffect(() => {
            console.log(property.civil);
        },[property.player]);
    
        return(
            <div className="flex gap-2">
                <button onClick={() => incrementPlayer()} className="border">+</button>
                <button onClick={() => decrementPlayer()} className="border">-</button>
            </div>
        );
    }
    
[/code]

上記のコードでは、`player`の増減を行うとU1・U2が発火し`civil`の計算、計算後の`civil`の値を出力するという仕掛けになっています。

1回目に `+` ボタンをクリックした場合`console.log`で 「 **3** 」 が出力される想定ですが、なぜか計算前の「 **2**
」が表示されます。

2回目に `+` ボタンをクリックすると「 **3** 」が表示されます。

順序としては

  1. `+` ボタンクリック
  2. `civil`の計算
  3. `console.log`で`civil`出力

という認識ですが、何故値が遅れて出力されるのでしょうか。ご教示ください。

### 環境

  * React 18.2.0
  * TypeScript 4.8.4
  * macOS Monterey 12.6
  * Chrome 106.0.5249.103


CC BY-SA 4.0
2022-10-10T03:02:46.293
91521
2022-10-10T11:42:35.817
2022-10-10T08:46:43.443
51662
51662
question
reactjs typescript
useEffectの挙動について


[useEffec フック API リファレンス – React](https://ja.reactjs.org/docs/hooks-
reference.html#useeffect) の「補足」にはこう書かれています

> この最適化を利用する場合、 **時間の経過とともに変化し副作用によって利用される、コンポーネントスコープの値（props や state など）**
> がすべて配列に含まれていることを確認してください。さもないとあなたのコードは以前のレンダー時の古い値を参照してしまうことになります。...

まさしく、この補足で指摘した状況になってしまっていると思われます。

`property.player`が変更されたとき、U1とU2が実行されるのですが、これらU1とU2が実行されるときの`property`は`useEffect()`によって実行が予約されたときの値です。どういうことかというと、U1で`setProperty`で`property`の値を変えても、その値は次のサイクルの`property`であって、既に実行が予約されているU2での`property`とは別のオブジェクトになると言うことです。別の言い方で言うと、U1が実行されるときの`property`と同じ`property`をU2が実行されるときも見に行っています。それは、`civil`を変更する前のものなので、相変わらず`2`になると言うことです。

もし、`civil`が変更されて、その結果を見たければ、次のように書くべきでしょう。

[code]

    // U2
    useEffect(() => {
        console.log(property.civil);
    },[property.civil]);
    
[/code]


CC BY-SA 4.0
2022-10-10T11:32:12.563
91528
2022-10-10T11:42:35.817
2022-10-10T11:42:35.817
7347
7347
91521
answer