この記事はエンジニア未経験で、新たにReact(Next.js)を学習している際に疑問に思ったことをまとめたものです。
今回は、Reactで通常の変数とStateを両方使用していた際、Stateは正しく値が更新されているのに、通常の変数は正しく値が格納されていない状態になりました。
調べていくと、Reactの再レンダリングの仕組みを理解せず、通常の変数を使っていたことが問題でした。
Reactで通常の変数とStateを両方利用した場合に起こった事象
Reactの学習のために、以下の様なコードを書いていました。
- ボタンを表示する。
- ボタンをクリックすると、1と2を交互に表示する
- この交互に表示される1と2を配列として保存する
- 配列に保存された数値(1と2が交互に格納されている)を記録として表示する
import { useState } from "react";
function Button(props) {
return(<button onClick={ props.onClick }> Button </button>)
}
export default function Home() {
const histories = [];
const [turn, setTurn] = useState(0);
const [result, setResult] = useState(0);
function handleClick() {
if(turn == 0) {
setResult(1);
setTurn(1);
histories.push(result);
console.log(histories);
} else {
setResult(2);
setTurn(0);
histories.push(result);
console.log(histories);
}
}
return(
<div>
<Button onClick={ ()=>handleClick() } />
<p>{ result }</p>
<ul>
{histories.map((history) => (
<li key={history}>{history}</li>
))}
</ul>
</div>
)
}
実行してボタンをクリックすると、画面には1と2の数値が交互に表示されますが、その下に表示したい1と2が交互に表示される記録がいつまで経っても表示されませんでした。
理由:ReactはStateが変更されると再レンダリングが発生する
ReactではStateが更新されると自動で再レンダリングが発生します。再レンダリングというと、再度対象となるプログラムを読み込みDOMに変更があれば更新する、という認識でしたが、具体的にどのようなことを行っているかよくわかっていませんでした。
デバッグを行った結果、再レンダリングが行われると具体的には対象となるコンポーネント(上記のHomeのコンポーネントと子要素であるButtonコンポーネント)を再度読み込み、実行していました。
Homeのコンポーネントでは交互に表示した数値を記録するためのhisotoriesを通常の変数として宣言していましたが、この部分が再度読み込まれるため、ボタンを押してhistoriesに数値を挿入した後、空の配列としてhistoriesを再度宣言するということが繰り返し発生していました。
そのため、画面には常に空のhistoriesの配列が表示され続け、画面には何も表示がされない状態となっていました。
まとめ
今回わかったことは、以下です。
ReactではStateが更新されると再レンダリングされる
具体的には、対象となるコンポーネントを再度実行する
そのため、通常の変数をコンポーネント内で宣言して利用していると、何度も上書きされてしまって値が正しく更新されませんでした。
どんな対応方法があるのか、そもそもこの通常の変数とStateを両方一つのコンポーネントで使うことに問題があるのか、これらについてはまたわかったら更新したいと思います。