Open5
Reactの基礎で曖昧なところを復習するメモ
その1
JavaScriptの特徴に伴う、再レンダリング時の注意点
useEffectは、依存配列の要素が「変更された」と判定されると再実行される。
Javascriptの性質上、「変更された」ことを判定する方法が、型によって異なる。
同じ物を再定義したとき、元のものと同じかどうかの判定が異なる
- 関数、オブジェクト → 新しい参照が生成されるたびに異なるものと見なされる
- number や string → プリミティブ型は値によって比較される
「オブジェクトや関数は新しい参照が生成されるたびに異なるものと見なされる」理由
- プリミティブ型(基本型):
- 数値、文字列、ブール値、null、undefined、symbolなど
- その値自体によって比較される。
- たとえば、二つの数値5があれば、これらは常に等しいと見なされる。
- オブジェクト型(参照型):
- オブジェクト、配列、関数など
- メモリ上のアドレス(参照)によって管理される。(値までは見ない)
- たとえば、{ a: 1 }を再定義した場合、aの値1は異なるメモリアドレスに格納されている。
つまり
JavaScriptでは新しく生成されたオブジェクトや関数(参照型)は、たとえ内部の内容が全く同じであっても、異なるメモリアドレス(参照)を持つため、異なるものとして扱われる。
じゃあどうするか
- useEffectの依存配列に、関数、オブジェクトを指定しないようにする
- useEffect内に定義する
- コンポーネントの外に定義する
- useEffectの外で中身のプリミティブな値を出して、それを依存配列に指定する
具体的に
その2
useEffectの依存配列に指定した変数がuseEffectの処理完了前に再度変更された場合の制御
クリーンアップ関数を活用する
- クリーンアップ関数とは以下2つのタイミングで実行される関数
- コンポーネントのアンマウント時
- 依存配列の値が変更された場合
useEffect(() => {
// 何らかの副作用を実行するコード
return () => {
// ここがクリーンアップ関数
};
}, [dependencies]);
- シーケンス
- 1回目のuseEffectの実行
- (useEffect内の処理完了前に)依存配列内要素が変更される
- 1回目のuseEffectのクリーンアップ関数実行
- クリーンアップ関数が完了したら、依存変更に伴う2回目のuseEffect実行
- 1回目のuseEffect完了(クリーンアップ関数で中止などしていなければ)
- 2回目のuseEffect完了
- 別ページへ移動などアンマウント
- 2回目のuseEffectのクリーンアップ関数実行
useEffect内でデータを取得する場合
-
困る
- 連続実行されると競合する
- 古い実行が時間かかると古いデータで上書きされる
-
どうするか
- クリーンアップ関数でフラグを使い、古い実行データがセットされないようにする
(例)
export default function Page() {
const [person, setPerson] = useState('Alice');
const [bio, setBio] = useState(null);
useEffect(() => {
let active = true;
async function startFetching() {
setBio(null);
const result = await fetchBio(person);
if (active) {
setBio(result);
}
}
startFetching();
return () => {
active = false;
}
}, [person]);
上記処理のシーケンス(2回目のuseEffectの挙動は以下図には記載していない)