本日は副作用フックでもある『useEffect』について初学様に向けて、さらに理解を深めてもらいたく記事にしました。
少し長くなっております、ご了承下さい。
Reactをマスターするなら、最も重要な概念を理解しておかなければならないのがこの副作用フックである『useEffect』となります。
『useEffect』機能を完全に理解するのは、困難な問題です。
このフックによって引き起こされる副作用を処理することは、初学者のうちは多少なり面倒で大変に思えたりするかもしれません。
ですが、後になればすべてが理にかなっていることを学んだと思う事でしょう。
『useEffect』フックを深く学ぶ事においては今後の貴方の力になってくれるはずです。
私の経験と情報を元に、貴方に向けて『useEffect』に関する深い学習の提供をこれから致します。
useEffectおさらい
少しおさらいをしましょう
主なuseEffect機能の効果
データ取得(フェッチ)
ローカルストレージからの読み込み
イベントリスナーなどの追加など
状態または小道具が変更された時の動作を実行
コンポーネントのアンマウント時にイベントリスナーをクリーンアップする
これらは一般的な使用例となります。
『useEffect』フックは、Reactライフサイクルイベントの部分的な代替品となります。
つまり『クラスコンポーネント』でのライフサイクルメソッドである、これらの行動複製することができ
『componentDidMount』
『componentDidUpdate』
『componentWillUnmount』
useEffectフックを含むコンポーネントの変更に対応が可能となります。
『useEffect』フックは、コードの重複を減らします。
これは、公式の『Reactドキュメント』では、1つのステートメントでライフサイクルメソッドの結果として生じる重複したコードを回避できることが提唱されています。
『useEffect』フックは2つの引数を取り1番目の引数はデフォルトですべてのレンダリング後に実行されるコールバックとなり
2番目の引数では、ターゲットの状態に変化があった時のみにコールバックされるフックとなります。
useEffect(() => { // 処理 }, [testCode1, testCode2]);
そしてオプションは『Dependency配列』です
依存関係である『以前の状態である値』と『現在の状態の値』を比較します。
2つの引数である値が一致しなかった場合に関しては、最初の引数で指定された値がコールバックとして実行されます。
『依存関係』は、『useEffect』の2番目の引数として指定するものとなり
依存関係を指定した場合、『useEffect』はその依存関係が変更されたときにのみトリガーされていきます
依存関係配列ではデフォルトでのコールバック動作をオーバーライドします
フックがスコープ内の他のすべてを無視するようになります。
なので『useEffect』フックでは 依存関係配列を正しく使用することが最も重要となります。
例えば、何も変更されていない場合でも『不要な再レンダリング』を防ぐことも重要となります。
そしてこの副作用フックは、再レンダリングが起こる度に実行されていきます。
依存関係配列を使用した場合、渡された依存関係が変更度に繰り返し実行されます。
空の配列では、フックが再び実行されないように無限ループの問題を解決しAPIの呼び出しが可能となり、
ライフサイクルメソッドとは対照的で、非同期として実行されるのでUIをブロック致しません。
『useEffect』フックは、さまざまな状況に応じて使用できるとう事です。
少し長くなりましたが、おさらいはこの辺りにしときましょう。
複数でのuseEffect
ここからが重要です、副作用フックでの注意点となります。
『useEffect』で複数の副作用がある場合、貴方はどう書きますか?
import React, { useState, useRef, useEffect } from "react"; const App = () => { const [Render, setRender] = useState("default value"); const renderRef = useRef(); useEffect(() => { console.log("useEffect"); document.Render = Render; }); const handleClick = () => setRender(renderRef.current.value); console.log("render"); return ( <div> <input ref={renderRef} /> <button onClick={handleClick}> Click here</button> </div> ); }
これはあまり最善の方法とは言えません。
複数での副作用の場合『useEffect』は1つにまとめて組み合わせるのではなく、『useEffect』を複数呼び出し分けなければいけません。
まずは、この副作用フックである『useEffect』を複数呼び出し使用し、バグやパフォーマンスの問題を引き起こし
無限ループとして実行してみます。
import React from 'react'; import { useEffect, useState, useRef} from 'react'; const App = () => { const [Render, setRender] = useState(" default value"); const renderRef = useRef(); useEffect(() => { console.log("useEffect"); document.Render = Render; }); useEffect(() => { console.log("local storage"); const useLocal = localStorage.getItem("local"); setRender(useLocal || []); }); console.log("render message"); const handleClick = () => setRender(renderRef.current.value); return ( <div> <input ref={renderRef} /> <button onClick={handleClick}> Click here </button> </div> ); } export default App;
DEMO
『useEffect』での全ての状態が変化した時に
『setRender』は他がトリガーされ状態は再度、更新 され続け無限ループとなります。
『useEffect』で依存関係配列を指定しなかった場合は、全ての機能『useEffect』が実行されます。
コード内の位置に基づき、次々と実行されることを意味致します。
『データの変更』『小道具の変更』『ユーザーがコンポーネントに対して確認した時』など
特定の条件後に副作用を実行しなければいけません。
複数での『useEffect』でこのような事が起きた場合 まずしなければいけない事『不要な再レンダリングを抑える事です』。
const [Render, setRender] = useState("default value"); const renderRef = useRef(); useEffect(() => { console.log("useEffect"); document.Render = Render; },[Render]); //無限ループ回避 useEffect(() => { console.log("local storage"); const useLocal = localStorage.getItem("local"); setRender(useLocal || []); }, []); //第一引数が実行される
コンソールで確認して下さい。
useEffect(() => { console.log("useEffect"); document.Render = Render; },[Render]); //無限ループ回避
再レンダリングが起きた後に不要なのをスキップさせたい時はこのように依存関係として配列を追加させてあげます。
useEffect(() => { console.log("local storage"); const useLocal = localStorage.getItem("local"); setRender(useLocal || []); }, []); //第一引数が実行される
第2引数に空の配列を渡した場合、値の変化があり初回のマウント時のみ、第1引数が実行されていきます。
注意して欲しいのが、この依存関係を省略したりして書いたりすると、バグが発生する可能性が非常に高くなります
クリーンアップ関数
import React from 'react'; import { useState, useEffect } from 'react'; const App = () => { const [count, setCount] = useState(0); useEffect(() => { const interval = setInterval( () => { setCount((value) => value + 1); }, 1000); }, []); return <p>counter {count}</p>; } const EffectUnmount = () => { const [unmount, setUnmount] = useState(false); constRrender = () => !unmount && <Counter />; return ( <div> <button onClick={() => setUnmount(true)}>Unmount counter</button> {Render()} </div> ); } export default App;
この場合、子のコンポーネントクリック時にReactはエラーをスローします。
毎秒事に関数を呼び出していますが、その間隔を解除しない為です
アンマウントにより、コンポーネントが破棄されても再びcountします。
クリーンアップをしてあげます
コールバックを返してあげるだけとなります。
const App = () => { const [count, setCount] = useState(0); useEffect(() => { const interval = setInterval( () => { setCount((value) => value + 1); }, 1000); return () => clearInterval(interval); //返す }, []); export default App;
これでコンポーネントが破棄される直前で毎回呼ばれる事になり依存関係の配列を空で指定した場合
クリーンアップ関数では、ライフサイクルと同じ機能を致します。
アンマウント時に毎回ではなく一度だけ呼びたい
const App = () => { const [count, setCount] = useState(0); useEffect(() => { const interval = setInterval( () => { setCount((value) => value + 1); }, 1000); return () => clearInterval(interval); //返す }, [count]); //一度だけ呼びだす export default App;
第2引数である依存関係の配列に渡してあげれば
クリーンアップ関数はアンマウント時に1度だけ呼ばれます。
クリーンアップ関数は、最初のレンダリング後に毎回呼び出されて前の副作用をクリーンアップし、その後に後続の副作用が実行されます。
useEffectフック内に複数の関数を含めても問題はありません。
同時に実行されますが、マウント時にリクエストをクリーンアップするようにしてください。
後のリクエスト処理が先に早く終了する場合がある為です。
本日は以上となります
『useEffect』は依存関係配列を使用してエフェクトの実行をコントロールします。
依存関係の1つが変更されるたびに、エフェクトが実行されていき、ほとんどの場合は状態が変化するたびに、一度だけではなくエフェクトを実行するようにコンポーネントを作成していく必要があるのです。
『useEffect』フックの基礎的な作成概念を理解することは、React開発者になりたい場合に習得するための次のステップへと行く重要なスキルとなっていると私、個人的意見では思います。
『useEffect』フックはまた記事にしていきます。
最後までお読み頂きありがとうございました。
貴方のスキルが次にステップアップされるのに当記事が役立つ事を願います。