useEffectって何?

プログラミング学習帳 プログラミング

useEffectは、useStateの次によく使われるReact Hooksです。useEffectは、一言で言えば「関数が実行されるタイミングを任意に決められる」ものです。外部APIを用いる場合、ユーザーが特定の操作をせずとも外部APIから取得した情報を表示できたり、便利なのでよく使われます。

React Hooksとは?

Hooksは”stateなどのReactの機能を、クラスを書かずに使える”ものとされています。深く考えずに、クラスを使うと冗長かつ可読性に欠けるものになりがちなものを、より可読性の高いものとしてHooksがある、という程度の理解で今はとどめておきます。

useEffectとは何か?

useEffectは、公式によると”useEffect内の関数はレンダーの結果が画面に反映された後で動作する”ものとされています。さて、レンダーとは何でしょうか。レンダー(render)とは、プログラムがコードを読み解いて表示すること、です。簡単に言えば、<button>button</button>と書いた時、<button>button</button>という文字をそのまま表示するのではなく、コードを解釈してbuttonという名前のbuttonを表示するのがレンダリングです。つまり、URLにアクセスする→プログラムが処理を読み込む→プログラムが処理を解釈して表示する(レンダリング)→useEffect内の処理を実行する、ということが可能になります。

例えば以下のコードがあったとします。

useEffect(() => {
 console.log("Hello, world")
}, [id])

第一引数:console.log()、第二引数:[id]です。この場合、[id]が更新されるたびにconsole.log()が実行されます。第一引数には実行したい関数や処理、第二引数には実行したいタイミングを書きます。つまるところ、useEffectは「処理が実行されるタイミングを、画面がロードされたタイミングではなく、ユーザーがクリックボタンを押したタイミングなど、任意でコントロールするための」もの、つまり「関数が実行されるタイミングを任意に決めたい時に使う」ものと覚えると分かり易い。useEffectを使うことで全体のレンダリングとuseEffect内の処理を切り離しています。

さて、useEffectが実行されるタイミングは二回あります。それは①ブラウザがコンポーネントで初めて表示されるとき。②useEffect内に記述した処理を実行したとき(ボタンを押した時など)。

ではなぜuseEffectは必要なのでしょうか。これは、useEffectを利用することで、外部のサーバからAPIを経由してデータを取得した後にuseEffect内の関数を実行することができたりするからです。まず、コンポーネントの初期化中に必ず一度実行されるので、ページを開いた直後にボタンをクリックしたりすることなく、外部から取得したデータを表示することができます。次に、第二引数が更新されるたびにuseEffectが実行されます。つまり、第二引数が更新されなければ、エラー表示が出ずデータも更新されていかない状況になります。

useEffectを使ったときのエラー

ブラウザ上で以下のようなエラーが起きることがあります。

Warning: Maximum update depth exceeded.

原因:useEffect内で状態が更新されるたびにuseEffectが再実行されるため、useEffectが無限ループしていることが原因。最初にマウントされたとき、更新されたとき、コンポーネントがアンマウントしたときにuseEffectは呼び出されます。第2引数を指定しない場合、useEffectは毎回呼び出されます。これにより、無限ループが発生する可能性がある、というエラーメッセージです。

対処法:useEffectの第2引数を修正する必要があります。useEffectの第二引数に空の配列を渡すことで、useEffectが最初に一度だけ実行されるようにします。或いは以下のようにuseEffectの第2引数に[id]を渡せば、idが変更されたときだけuseEffectが呼び出され、無限ループを回避できます。

useEffect(() => {
  //コード
}, [id]);

useEffectの第二引数で注意すること

useEffectの第二引数では、関数が実行されるタイミングを記述すると説明しました。例えば[id]とすれば、idの値が変化するたびにuseEffect内の処理が実行されます。一方で、この第二引数では、「100==100は変化なし」ですが、「{}=={}は変化あり」とされてuseEffectが実行されてしまいます。違いは、文字列/数値/真偽値/undefinedはプリミティブ型で、オブジェクト/配列は非プリミティブ型である、ということです。プリミティブ型では値が変わらなければ変化なしとされますが、非プリミティブ型では値が変化しなくても変化あり、と判定されてuseEffectが実行されます。

対処法

不正解

const [state, setState] = useState({
 id: number,
 message: "",
});
useEffect(() => {
 console.log("Hello, world");
}, [state]);

正解

useEffect(() => {
 console.log("Hello, world");
}, [state.id, state.message]);

つまるところ、非プリミティブ型を分解して、素直にプリミティブ型で記述しましょう、ということ。

コメント

タイトルとURLをコピーしました