試行その1 #
import { useState, useEffect } from "react";
export default function Component() {
  const [int, setInt] = useState<number>();
  useEffect(() => setInt(1), []);
  useEffect(() => setInt(3), []);
  useEffect(() => setInt(2), []);
  console.log(int);
  return <></>;
}
結果その1 #
コンソールには以下の順番で出力されます。
undefined
2
初回レンダリングは undefined となっているとして、次のレンダリングでは最後に指定された setState の結果が有効となるようです。
試行その2 #
子のコンポーネントでも setState するとどうなるでしょうか。
import { useState, useEffect } from "react";
export default function Parent() {
  const [int, setInt] = useState<number>();
  useEffect(() => setInt(1), []);
  useEffect(() => setInt(3), []);
  useEffect(() => setInt(2), []);
  console.log(`Parent ${int}`);
  return <Child int={int} setInt={setInt} />;
}
type Props = {
  int: number | undefined;
  setInt: React.Dispatch<React.SetStateAction<number | undefined>>;
};
function Child(props: Props) {
  const { int, setInt } = props;
  useEffect(() => setInt(10), []);
  useEffect(() => setInt(30), []);
  useEffect(() => setInt(20), []);
  console.log(`Child ${int}`);
  return <></>;
}
結果その2 #
コンソールには以下の順番で出力されます。
Parent undefined
Child undefined
Parent 2
Child 2
最終結果は 20 になると思っていたので予想外でした。
試行その3 #
どの順番で useEffect が動いているかを確認してみます。
import { useRef, useEffect } from "react";
export default function Parent() {
  const int = useRef<number[]>([]);
  useEffect(() => {
    int.current.push(1);
  }, []);
  useEffect(() => {
    int.current.push(3);
  }, []);
  useEffect(() => {
    int.current.push(2);
  }, []);
  setTimeout(() => {
    console.log(int.current);
  }, 1000);
  return <Child int={int} />;
}
function Child({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(10);
  }, []);
  useEffect(() => {
    int.current.push(30);
  }, []);
  useEffect(() => {
    int.current.push(20);
  }, []);
  return <></>;
}
結果その3 #
コンソールには以下の順番で出力されます。
[]
[10, 30, 20, 1, 3, 2]
試行その4 #
孫コンポーネントまで用意し同じことを試してみます。
import { useRef, useEffect } from "react";
export default function Parent() {
  const int = useRef<number[]>([]);
  useEffect(() => {
    int.current.push(1);
  }, []);
  useEffect(() => {
    int.current.push(3);
  }, []);
  useEffect(() => {
    int.current.push(2);
  }, []);
  setTimeout(() => {
    console.log(int.current);
  }, 1000);
  return <Child int={int} />;
}
function Child({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(10);
  }, []);
  useEffect(() => {
    int.current.push(30);
  }, []);
  useEffect(() => {
    int.current.push(20);
  }, []);
  return <Grandchild int={int} />;
}
function Grandchild({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(100);
  }, []);
  useEffect(() => {
    int.current.push(300);
  }, []);
  useEffect(() => {
    int.current.push(200);
  }, []);
  return <></>;
}
結果その4 #
コンソールには以下の順番で出力されます。
[]
[100, 300, 200, 10, 30, 20, 1, 3, 2]
試行その5 #
親は2つの子を持ち、子は2つの孫を持つ状態で実行してみます。
import { useRef, useEffect } from "react";
export default function Parent() {
  const int = useRef<number[]>([]);
  useEffect(() => {
    int.current.push(1);
  }, []);
  setTimeout(() => {
    console.log(int.current);
  }, 1000);
  return (
    <>
      <Child1 int={int} />;
      <Child2 int={int} />;
    </>
  );
}
function Child1({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(10);
  }, []);
  return (
    <>
      <Grandchild11 int={int} />;
      <Grandchild12 int={int} />;
    </>
  );
}
function Child2({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(20);
  }, []);
  return (
    <>
      <Grandchild21 int={int} />;
      <Grandchild22 int={int} />;
    </>
  );
}
function Grandchild11({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(110);
  }, []);
  return <></>;
}
function Grandchild12({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(120);
  }, []);
  return <></>;
}
function Grandchild21({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(210);
  }, []);
  return <></>;
}
function Grandchild22({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(220);
  }, []);
  return <></>;
}
結果その5 #
コンソールには以下の順番で出力されます。
[]
[110, 120, 10, 210, 220, 20, 1]
試行その6 #
しつこくなってきたのでこれで最後です。親は2つの子を持ち、子は2つの孫を持ち、孫は2つのひ孫を持つ状態で実行してみます。
import { useRef, useEffect } from "react";
export default function Parent() {
  const int = useRef<number[]>([]);
  useEffect(() => {
    int.current.push(1);
  }, []);
  setTimeout(() => {
    console.log(int.current);
  }, 1000);
  return (
    <>
      <Child1 int={int} />;
      <Child2 int={int} />;
    </>
  );
}
function Child1({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(10);
  }, []);
  return (
    <>
      <Grandchild11 int={int} />;
      <Grandchild12 int={int} />;
    </>
  );
}
function Child2({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(20);
  }, []);
  return (
    <>
      <Grandchild21 int={int} />;
      <Grandchild22 int={int} />;
    </>
  );
}
function Grandchild11({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(110);
  }, []);
  return (
    <>
      <GreatGrandchild111 int={int} />
      <GreatGrandchild112 int={int} />
    </>
  );
}
function Grandchild12({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(120);
  }, []);
  return (
    <>
      <GreatGrandchild121 int={int} />
      <GreatGrandchild122 int={int} />
    </>
  );
}
function Grandchild21({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(210);
  }, []);
  return (
    <>
      <GreatGrandchild211 int={int} />
      <GreatGrandchild212 int={int} />
    </>
  );
}
function Grandchild22({ int }: { int: React.MutableRefObject<number[]> }) {
  useEffect(() => {
    int.current.push(220);
  }, []);
  return (
    <>
      <GreatGrandchild221 int={int} />
      <GreatGrandchild222 int={int} />
    </>
  );
}
function GreatGrandchild111({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(1110);
  }, []);
  return <></>;
}
function GreatGrandchild112({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(1120);
  }, []);
  return <></>;
}
function GreatGrandchild121({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(1210);
  }, []);
  return <></>;
}
function GreatGrandchild122({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(1220);
  }, []);
  return <></>;
}
function GreatGrandchild211({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(2110);
  }, []);
  return <></>;
}
function GreatGrandchild212({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(2120);
  }, []);
  return <></>;
}
function GreatGrandchild221({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(2210);
  }, []);
  return <></>;
}
function GreatGrandchild222({
  int,
}: {
  int: React.MutableRefObject<number[]>;
}) {
  useEffect(() => {
    int.current.push(2220);
  }, []);
  return <></>;
}
結果その6 #
コンソールには以下の順番で出力されます。
[]
[1110, 1120, 110, 1210, 1220, 120, 10, 2110, 2120, 210, 2210, 2220, 220, 20, 1]
まとめ #
最後の試行結果を視覚化して終わりとします。
以下の階層構造・順番でコンポーネントが構成されており、それぞれで useEffect を実行している場合は、
Parent
 ├ Child1
 │  ├ Grandchild11
 │  │  ├ GreatGrandchild111
 │  │  └ GreatGrandchild112
 │  └ Grandchild12
 │     ├ GreatGrandchild121
 │     └ GreatGrandchild122
 └ Child2
    ├ Grandchild21
    │  ├ GreatGrandchild211
    │  └ GreatGrandchild212
    └ Grandchild22
       ├ GreatGrandchild221
       └ GreatGrandchild222
以下の順番で useEffect が実行されます。
GreatGrandchild111
GreatGrandchild112
Grandchild11
GreatGrandchild121
GreatGrandchild122
Grandchild12
Child1
GreatGrandchild211
GreatGrandchild212
Grandchild21
GreatGrandchild221
GreatGrandchild222
Grandchild22
Child2
Parent