Reactの基本的な仕組みであるuseEffectについて学習した。
Table of contents
Open Table of contents
概要
useEffectとは
useEffectは、Reactのフックの一つで、コンポーネントの副作用を管理するための機能。レンダリング後に副作用を実行するために使用される。
副作用とは
副作用とは、コンポーネントのレンダリング以外で行う処理(タイマー処理、イベントリスナー、localStorageへの保存やAPI取得など)を指す。
useEffectの使い方
基本形
以下は読み込むとconsole.logで「test」が出力されるコンポーネント。
useEffectの第一引数に実行したい処理、第二引数には依存配列を入れる。
この場合、依存配列は空([])にすると、コンポーネントの初回レンダリング時にのみ実行される。
再レンダリング時に毎回実行したい場合は依存配列「[]」を削除する。
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('初回のみ');
}, []);
}
export default MyComponent;
依存配列の指定
以下はボタンをクリックするとカウントが変更されるコンポーネント。
クリック時にuseStateのsetCountを呼び出してカウントを更新。
useEffectの第二引数に依存配列を指定することで、依存する値が変更されたときにEffectを実行する。
import React, { useEffect, useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`countが変更されたときに実行: ${count}`);
}, [count]);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}
export default MyComponent;
開始、終了の処理
以下はEffectの開始時にconsole.log('開始')を実行、コンポーネントのアンマウント時から1秒後にconsole.log('終了')を実行するコンポーネント。
setTimeoutを使って非同期処理を行っている。
returnで返した関数はクリーンアップ関数と呼ばれる。
コンポーネントのアンマウント(削除)時に、Effectのクリーンアップ関数が自動的に実行される。
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('開始');
return () => {
setTimeout(() => {
console.log('終了');
}, 1000);
};
}, []);
}
export default MyComponent;
タイマー処理
以下はEffectの開始時にconst intervalId = setInterval(() => {})を実行するコンポーネント。1秒おきにカウントアップ。
setIntervalを使って非同期処理を行っている。
終了時にclearIntervalを使ってタイマーを停止する。
import React, { useEffect, useState } from 'react';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearInterval(intervalId);
}, []);
return (
<div>
<p>{count}</p>
</div>
);
}
export default Timer;
次はEffectの開始時にconst time = setInterval(() => {})を実行する点は同じだが、現在の日付、時刻を表示するコンポーネント。
先の記述例同様、setIntervalを使って非同期処理、終了時にclearIntervalを使用。
import React, { useEffect, useState } from 'react';
function Timer() {
const [time, setTime] = useState(new Date().toLocaleString());
useEffect(() => {
const intervalId = setInterval(() => {
setTime(new Date().toLocaleString());
}, 1000);
return () => clearInterval(intervalId);
}, []);
return (
<div>
<p>{time}</p>
</div>
);
}
export default Timer;
localStorageの処理
以下は、まずuseStateでコンポーネントのStateを初期化し、localStorageからデータを取得。
Effectの開始時にカウントアップを実行し、その後、localStorageへの保存を行う。
import React, { useEffect, useState } from 'react';
function LocalStorageTest() {
// useStateの初期化時にlocaStorageからデータを取得。
// データがなければ0を入れておく
const [count, setCount] = useState(() => {
const savedCount = localStorage.getItem('count');
return savedCount !== null ? Number(savedCount) : 0;
});
// カウントアップ
useEffect(() => {
const intervalInt = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearInterval(intervalInt);
}, []);
// localStorageへ保存
useEffect(() => {
localStorage.setItem('count', String(count));
}, [count]);
return (
<div>
<p>{count}</p>
</div>
);
}
export default LocalStorageTest;
注意点
開発環境(StrictMode)では useEffect が2回実行されたように見えることがあります。 これは React が副作用の問題を検出するための開発専用の挙動です。本番環境では1回のみ実行されます。
まとめ
useEffectを使用すると、コンポーネントのレンダリング後に副作用を実行できる。 依存配列を利用することで、初回のみ実行したり、特定のState変更時に実行したりできる。