以下のような変数を関数コンポーネント内部で定義したとします。その後に // ..
でuseEffectでimagesOnWaitingList
にAPI経由で取得した値を配列に格納することがありました。その変数を後でボタンクリックイベントなどで参照すると、imagesOnWaitingList
が何らかのタイミングで空[]
になることがあり、原因がわからず時間がかかりました。
const Home: NextPage = () => {
let imagesOnWaitingList: Array<ImageMetadataOnWaitingList> = []
// ..
}
解決策と原因
関数コンポーネントの外側で変数を定義することで解決しました。関数コンポーネントのレンダリングの仕組みはわかりませんが、おそらく他のstateful valueが更新されたタイミングで関数コンポーネントが呼ばれると理解しています。そのため、stateful value出ない変数の初期化処理を関数に含めた場合、複数回関数コンポーネントがコールされて予期せぬ初期値が得られたのでしょう。
let imagesOnWaitingList: Array<ImageMetadataOnWaitingList> = []
const Home: NextPage = () => {
// ..
}
Next/Reactとstateful values
そもそも、imagesOnWaitingList
は、インターフェース上に表示するものでもないので、わざわざstatefulにする必要を感じていなかったのです。しかし、このimagesOnWaitingList
を更新する条件は他のstateful valueに依存していたので、バグを避けるために、そのような時はstateful valueを使う、という方針がいいのでしょうか?
Stateful valueというものは、あくまで目に見える(インターフェースにレンダーする)ものに使うだけでなく、他のstateful valueの影響を受ける内部の状態にも使うべきなのかもしれません。React/Nextは変数の更新のタイミングやレンダリングなどで注意すべき点が多いと思います。
また、関数コンポーネントではuseStateを用いてローカルの変数を定義することができることを考えても、ローカルでの状態はなるべくstateful valueに統一するのが、関数のコールによる予期せぬ初期化に対する根治術かもしれませんね。
ステートフックによって、React の歴史上はじめて、React の関数コンポーネントにローカル state を加えることができます!