⬛️
Reactの基本のあれこれを`console.log()`で確認してみた
console.log
してみた
1. 親と子でのState更新と、再レンダリング
確認事項
- 親と子のstateが変更される際の、再レンダリングの発生を確認する。
コード
- 親コンポーネント:
App
- 子コンポーネント:
-
ChildA
(App
からpropsを受け取る)-
count
stateを持つ
-
-
ChildB
(App
からpropsを受け取らない)
-
const ChildA = ({ state }) => {
const [count, setCount] = React.useState(0);
+ console.log(`rendering in child A component: count has ${count}`);
return (
...
<button onClick={() => setCount(count + 1)}>Child A: Count-up</button>
...
);
};
const ChildB = () => {
+ console.log("rendering in child B component");
return <div>Child B doesn't have props passed from the parent</div>;
};
export default function App() {
const [state, setState] = React.useState(false);
+ console.log("rendering in parent component");
return (
<div className="App">
...
<button onClick={() => setState(!state)}>Update the parent state</button>
...
<ChildA state={state} />
...
<ChildB />
</div>
);
}
コンソールログ
Console
<!-- 1. Initial rendering -->
rendering in parent component
rendering in child A component: count has 0
rendering in child B component
<!-- 2. Update the parent state -->
rendering in parent component
rendering in child A component: count has 0
rendering in child B component
<!-- 3. Update the child A state -->
rendering in child A component: count has 1
<!-- 4. Update the parent state -->
rendering in parent component
rendering in child A component: count has 1
rendering in child B component
確認したこと
- 親コンポーネントのstateが変更された際に、propsの受け渡しの有無に関わらず、親、子どちらのコンポーネントでも再レンダリングが発生する。(No.2参照)
- 子コンポーネントでのstateの変更は、そのコンポーネントだけ再レンダリングが発生する。(No.3参照)
- 親コンポーネントの再レンダリングによる、子コンポーネントの再レンダリングでは子コンポーネントのstateは最新が維持される。(No.4参照)
サンドボックス
2. useState初期値 vs 遅延初期化
確認事項
- stateの遅延初期化が初期レンダリング時の時のみ、呼び出されていることを確認する。
- 一方、初期値は再レンダリング毎に呼び出されていることを確認する。
コード
- 親コンポーネント:
App
- 子コンポーネント:
Child
-
childStateA
state: 遅延初期化 -
childStateB
state: 初期値
-
const someExpensiveCalculation = (number, type) => {
+ console.log(`in the ${type} initial state`);
return number * 10;
};
const Child = ({ number }) => {
const [childStateA, setChildStateA] = React.useState(() => {
return someExpensiveCalculation(number, "lazy");
});
const [childStateB, setChildStateB] = React.useState(
someExpensiveCalculation(number, "default")
);
console.log(
+ `rendering in child component: A: ${childStateA}, B: ${childStateB}`
);
return (
<>
<p>{`The childStateA is ${childStateA}`}</p>
<button onClick={() => setChildStateA(childStateA + 1)}>
Child A: Count-up
</button>
<p>{`The childStateB is ${childStateB}`}</p>
<button onClick={() => setChildStateB(childStateB + 1)}>
Child B: Count-up
</button>
</>
);
};
export default function App() {
const [state, setState] = React.useState(false);
return (
<div className="App">
<button onClick={() => setState(!state)}>Update the parent state</button>
<Child number={10} />
</div>
);
}
コンソールログ
Console
<!-- 1. Initial rendering -->
in the lazy initial state
in the default initial state
rendering in child component: A: 100, B: 100
<!-- 2. Parent state update -->
in the default initial state
rendering in child component: A: 100, B: 100
<!-- 3. Child state A update -->
in the default initial state
rendering in child component: A: 101, B: 100
<!-- 3. Child state B update -->
in the default initial state
rendering in child component: A: 101, B: 101
<!-- 4. Parent state update -->
in the default initial state
rendering in child component: A: 101, B: 101
確認したこと
- 遅延初期化を行えば、初期レンダリング時のみ
someExpensiveCalculation()
が呼ばれていて、再レンダリングでは無視される。 - 一方、遅延初期化でなく値を渡したときには、再レンダリングが走るたび
someExpensiveCalculation()
が呼ばれている。
サンドボックス
useEffect
のタイミング
3. 確認事項
-
useEffect
に渡された関数はレンダーの結果が画面に反映された後に動作することを確認する。
コード
-
state
を依存する値としているuseEffect
にて、dataをfetch後、message
ステートを更新する。
const dataFetchMock = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("setMessage executed in useEffect");
}, 1500);
});
export default function App() {
const [message, setMessage] = React.useState();
const [state, setState] = React.useState(false);
React.useEffect(() => {
+ console.log(`in useEffect. state: ${state}`);
dataFetchMock.then((value) => {
setMessage(value);
});
}, [state]);
+ console.log(`rendering: just before return jsx. message: ${message}`);
return (
<div className="App">
<button onClick={() => setState(!state)}>Update the parent state</button>
<p>{message === undefined ? "undefined" : message}</p>
</div>
);
}
コンソールログ
Console
<!-- 1. Initial rendering -->
rendering: just before return jsx. message: undefined
in useEffect. state: false
rendering: just before return jsx. message: setMessage executed in useEffect
<!-- 2. State(dependency of the useEffect) updated -->
rendering: just before return jsx. message: setMessage executed in useEffect
in useEffect. state: true
rendering: just before return jsx. message: setMessage executed in useEffect
確認したこと
-
useEffectはレンダーの後に動作している。
- 初期レンダリングで(No.1参照)、まずはレンダリング =>
useEffect
=>useEffect
内でのmessage
ステートの変更で再度レンダリング -
useEffect
の依存配列に含まれているstateの更新(No.2参照)では、stateの更新によるレンダリング =>useEffect
=>useEffect
内でのmessage
ステートの変更で再レンダリング
- 初期レンダリングで(No.1参照)、まずはレンダリング =>
サンドボックス
まとめ
なんとなくの理解で使うことのできるReact。
だけど、再レンダーのタイミングなど、自分で確認してみることは有意義だと思った。
This article is also available in English: https://dev.to/takuyakikuchi/i-checked-this-and-that-of-react-basics-with-consolelog-4850
Discussion