当サイトは、アフィリエイト広告を利用しています
useEffectについて書く前に、reactコンポーネントのライフサイクルと
ライフサイクルメソッドについてまとめる。
ライフサイクルとは、ざっくりいうとReactのコンポーネントが生まれてから成長し、
最終的に死ぬまでの一連の過程のこと。
reactコンポーネントのライフサイクルには大きく3つの期間
ライフサイクルの3つの期間(Mounting・Updating・Unmounting)の
それぞれ期間で使用できるメソッドをライフサイクルメソッドという。
クラスコンポーネントで書いた場合のサンプルを記述する
ただ今後、公式でもクラスコンポーネントではなく関数コンポーネントを推奨しているので
書くことはなくなってくると思う。
一番始めのレンダーされた直後に一度だけコールされるメソッド。
componentDidMount() {console.log("componentDidMount")}
コンポーネントが表示された後、ユーザーによる操作(クリック、スクロールなど)によって
再レンダーされた直後にコールされるメソッド。
componentDidUpdate() {console.log("componentDidUpdate")}
画面遷移などして、コンポーネントが破棄される前にコールされるメソッドです
componentWillUnmount() {console.log("componentDidMount")}
Reactの基礎【ライフサイクル】
【React Hooks】ライフサイクルについて
Reactのライフサイクルメソッドについて
useEffectとは関数コンポーネントにライフサイクルメソッドを持たせるための機能みたいなもの。
ライフサイクルメソッドは以前はクラスコンポーネントでしか記述できなかったが
今ではuseEffectを使えば、同様の動きを関数コンポーネントで実装することができる
useEffectなどのReactのフックを使う時は、正しく動作させるために
を順守する必要がある。
詳しくは下記でまとめている
useEffectの基本構文
useEffect(() => {// 任意の処理// 任意のクリーンアップ処理}, [/* 任意の変数 */]);
第一引数にはコールバック関数を設定し、これはレンダリング後に
動作する。
useEffect(() => {// 任意の処理console.log(`count${count}`);});
コールバック関数でreturnで関数を返すことでそれをクリーンアップの処理と みなすことができる。 クリーン処理の実行タイミングも第二引数で決まる。
useEffect(() => {// 任意の処理console.log(`count${count}`);// 任意のクリーンアップ処理return ()=>{console.log(`cleanUp`);}},[count]);
クリーンアップ処理もcountの値がかわったタイミングで実行される。
処理の実行順としては下記になる
第二引数は副作用処理(第一引数の関数)をどのタイミングで実行するかを
決めることができる。
※どのタイミングでもレンダリング後に実行される
第二引数の配列には下記を設定することができる
レンダリング後に毎回実行される
useEffect(() => {console.log(`count${count}`);});
初期マウント時(レンダリング後)に1回のみ実行される
useEffect(() => {console.log(`count${count}`);},[]);
countが変わり、画面が再レンダリングされた後に実行される
useEffect(() => {console.log(`count${count}`);},[count]);
useEffectの実行パターンサンプルをいくつか下記に記載する。
下記のただカウントするだけのcounter.jsxをuseEffectで書き換えていってみる
import "./styles.css";import React, { useState } from "react";export default function App() {//useStateconst [count,setCount] = useState(0)//カウントアップconst countUp = ()=>{setCount((prevCount)=>prevCount+1)}return (<div className="App"><div><h2>カウンター</h2><button onClick={countUp} >click me</button>{count}</div></div>);}
useEffectで初回マウント時(初回レンダリング後)のみlogが画面に出力するように書き換える。
useEffectの第二引数に空配列を設定した場合は、マウント時の1回のみ実行される。
ライフサイクルメソッドでいうとcomponentDidMountと同じ動きになる。
import "./styles.css";import React, { useEffect, useState } from "react";export default function App() {//カウンターのstateconst [count,setCount] = useState(0)//ログのstateconst [log,setLog] = useState([])// useEffectでマウント時のみlog出力する.// マウント時のみ実行する場合は第二引数に空配列を設定するuseEffect(() => {setLog((prevLog)=>[...prevLog,`count${count}`])},[]);// カウントアップ関数const countUp = ()=>{setCount((prevCount)=>prevCount+1)}return (<div className="App"><div><h2>カウンター</h2><button onClick={countUp} >click me</button>{count}</div><div><h2>log</h2>{log.map((log=><div key={log}>{log}</div>))}</div></div>);}
useEffectで毎レンダリング時にlogが出力するように書き換える。
useEffectの第二引数に設定しなかった場合は、毎レンダリング時に実行される。
ライフサイクルメソッドでいうとcomponentDidUpdateと同じ動きになる。
import "./styles.css";import React, { useEffect, useState } from "react";export default function App() {//カウンターのstateconst [count, setCount] = useState(0);// useEffectでレンダリング時に毎回log出力する.// 毎レンダリング時実行する場合は第二引数を設定しないuseEffect(() => {console.log(`count${count}`);});// カウントアップ関数const countUp = () => {setCount((prevCount) => prevCount + 1);};return (<div className="App"><div><h2>カウンター</h2><button onClick={countUp}>click me</button>{count}</div></div>);}
※画面にlogを出した場合、その度、レンダリングが行われるため無限ループするので
コンソールに出している。
useEffectでstateのcountを監視して変更があった場合のみ
ログ出力する。
import "./styles.css";import React, { useEffect, useState } from "react";export default function App() {//カウンターのstateconst [count,setCount] = useState(0)//ログーのstateconst [log,setLog] = useState([])// useEffectでcountに変更が合った場合のみlog出力する.// stateが変わった時のみ実行する場合は配列にそのstateを入れるuseEffect(() => {setLog((prevLog)=>[...prevLog,`count${count}`])},[count]);// カウントアップ関数const countUp = ()=>{setCount((prevCount)=>prevCount+1)}return (<div className="App"><div><h2>カウンター</h2><button onClick={countUp} >click me</button>{count}</div><div><h2>log</h2>{log.map((log=><div key={log}>{log}</div>))}</div></div>);}
useEffect部分の処理の順としては下記となる。
上記の処理にクリーンアップ処理を追加して
stateの値が特定の値になったタイミングで初期化が行われるようにする。
import "./styles.css";import React, { useEffect, useState } from "react";export default function App() {//カウンターのstateconst [count, setCount] = useState(1);//ログーのstateconst [log, setLog] = useState([]);// useEffectでcountに変更が合った場合のみlog出力する.// stateが変わった時のみ実行する場合は配列にそのstateを入れるuseEffect(() => {// ログを表示するsetLog((prevLog) => [...prevLog, `count${count}`]);// クリーンアップ処理// countに変更があった場合に実行されるreturn () => {// ログが4行になったら初期化setLog((prevLog) =>prevLog.length === 4 ? [] : prevLog.map((log) => log));// カウントが4になったら初期化setCount((prevCount) => (prevCount === 4 ? 1 : prevCount));};}, [count]);// カウントアップ関数const countUp = () => {setCount((prevCount) => prevCount + 1);};return (<div className="App"><div><h2>カウンター</h2><button onClick={countUp}>click me</button>{count}</div><div><h2>log</h2>{log.map((log) => (<div key={log}>{log}</div>))}</div></div>);}
useEffect部分の処理の順としては下記となる。
Counterコンポーネントがアンマウントされた時のみ、
画面のログに「LOG END」が出力される。
ライフサイクルメソッドのcomponentWillUnmountと同じ動きになる
Counterコンポーネント内にstateでLogを持たせた場合は、アンマウントした時に
Counterコンポーネントが破棄されてLogも消えてしまい、「LOG END」が出力できないため
stateのlogは親コンポーネントに持たせ、propsでCounterコンポーネントに渡している。
import "./styles.css";import React, { useEffect, useState } from "react";// Counterコンポーネントconst Counter = ({ log, setLog }) => {//カウンターのstateconst [count, setCount] = useState(0);useEffect(() => {// Counterコンポーネントがアンマウント時のみに実行// returnで関数を返した場合はアンマウント時に実行されるreturn () => setLog((prev) => [...prev, `LOG END`]);}, []);// カウントアップ関数const countUp = () => {setCount((prevCount) => prevCount + 1);};return (<div className="App"><div><h2>カウンター</h2><button onClick={countUp}>click me</button>{count}</div>{log.map((log, index) => (<div style={{ textAlign: "center" }} key={index}>{log}</div>))}</div>);};export default function App() {const [state, setState] = useState(true);//ログのstateconst [log, setLog] = useState([]);// Counterコンポーネントに渡すpropsをまとめるconst prop = { log, setLog };return (<div>{state && (<div>{/* stateを切り替えCounterを破棄してアンマウントを実行させる */}<button onClick={() => setState(!state)}>アンマウント実行</button>{/* stateのlogを渡す */}<Counter {...prop} /></div>)}{!state && (<div><button onClick={() => setState(!state)}>戻る</button></div>)}</div>);}
useEffect部分の処理の順としては下記となる。
useEffect部分を少し詳しまとめる
useEffect(() => {// Counterコンポーネントがアンマウント時のみに実行// returnで関数を返した場合はアンマウント時に実行されるreturn () => setLog((prev) => [...prev, `LOG END`]);}, []);
下記以外のソースは「アンマウント時のみ実行する」と同じ。
useEffect第一引数で任意の処理とクリーンアップ処理を書く
useEffect(() => {// マウント時のみ実行されるsetLog((prev) => [...prev, `start`]);// Counterコンポーネントがアンマウント時のみに実行// returnで関数を返した場合はアンマウント時に実行されるreturn () => setLog((prev) => [...prev, `LOG END`]);}, []);
useEffect部分の処理の順としては下記となる。
【React Hooks】useEffectの基本的な動きを理解して使いこなそう
React HooksのuseEffectで関数コンポーネントにライフサイクルを持たせる
useEffectフックのしくみ
【useEffect】React hookが便利すぎる