Effect を理解するためには次のロジックを押さえておく必要がある。
レンダーコード
コンポーネントのトップレベルにあるもので、
props
やstate
から画面に表示したい JSX を返す場所。レンダーコードは純粋関数*1。-
コンポーネント内にネストされた関数で、計算だけでなく実行する。特定のユーザアクションによってプログラムの状態を変更する(副作用を与える)。
上記だけでは十分でないシーンがある。例えば、サーバとの通信を維持するような処理は、純粋関数ではないのでレンダーコードへは書けないし、『クリックする』といった特定のイベントもない。
そこで使うのが Effect で、特定のイベントによってではなく、レンダー自体によって引き起こされる副作用を指定するためのもので、コミットの最後に画面が更新された後に実行される。
Effect は React 外の外部システムとの同期が必要な場合*2において使用されるものと覚えておけば良さそうで、そうしたシーン以外では不要。
Effect の書き方
Effect は大きく3つのフローからなる。
- Effect を宣言する
- Effect の依存値の配列を指定する
- 必要に応じてクリーンアップを追加する
Effect を宣言する
まずは useEffect
をインポートする:
import { useEffect } from "react";
次に、コンポーネントのトップレベルで呼び出し、Effect 内にコードを記述する:
function MyComponent() { useEffect(() => { // Recat はレンダーされた後に、Effect 内の処理を実行する // レンダーされる度にやってほしい処理を書く }); return <div />; }
Effect で囲わない場合、レンダー中に実行されることになる。レンダーコードは純粋関数である必要があり、副作用があるようなものは記述できない。
そこで、useEffect
でラップし、レンダーの計算処理の外に出してしまう。そうすることで、React は Effect の処理はレンダー後に実行すべき処理と認識し、レンダーが完了した後に実行するようにできる。
Effect の依存値の配列を指定する
デフォルトでは、Effect は全てのレンダー後に実行されるが、望ましくない場合がある(キーストロークごとのレンダー後に何かを実行するなど)。
そこで、useEffect
の呼び出しの第2引数に依存値の配列を渡し、React に再実行不要な Effect を指示することができる:
useEffect(() => { // ... }, []);
Effect の処理が依存する変数を配列内に定義することで、渡した変数が変化するときのみ Effect の処理が実行するように React に教えることができる。
useEffect(() => { if (isPlaying) { // 依存変数はここで使われている // ... } else { // ... } }, [isPlaying]); // Effect が依存する変数を書く
React は配列内の全ての値が前回レンダー時と同じ場合のみ Effect の処理をスキップする。
必要に応じてクリーンアップを追加する
クリーンアップ関数は、次のタイミングで React が呼び出す関数:
- Effect が再度実行される前
- コンポーネントがアンマウント(削除) される時の最後
書き方は次のように return
でクリーン関数を返すようにする:
useEffect(() => { const connection = createConnection(); connection.connect(); return () => { connection.disconnect(); }; }, []);
上記コードにおいて クリーンアップが無い場合、再マウント時に connection.connect()
が呼ばれ2つ目の接続などが実行されてしまう。
それをクリーンアップ関数を用いることで、一度切断してから接続というフローを取らせることができるようになる。
ちなみに React ではそうした不具合を見つけやすくるために開発環境においては、初回マウント直後に全てのコンポーネントを一度だけ再マウントし気づきやすくしているらしい👀