ReactuseEffect
useEffect
フックを使用すると、コンポーネントで副作用を実行できます。
副作用の例としては、データのフェッチ、DOMの直接更新、タイマーなどがあります。
useEffect
は、2つの引数を受け入れます。 2 番目の引数はオプションです。
useEffect(<function>, <dependency>)
例としてタイマーを使用してみましょう。
例:
setTimeout()
を使用して、最初のレンダリング後の1秒をカウントするには:
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
});
return <h1>I've rendered {count} times!</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);
ちょっと待って!!一度だけカウントするはずなのに、カウントし続けます!
useEffect
は、すべてのレンダリングで実行されます。つまり、カウントが変化するとレンダリングが発生し別の効果がトリガーされます。
これは私たちが望んでいるものではありません。副作用が発生するタイミングを制御するには、いくつかの方法があります。
配列を受け入れる2番目のパラメータを常に含める必要があります。オプションで、この配列内の useEffectに依存関係を渡すことができます。
例
1.依存関係が無い場合:
useEffect(() => {
//Runs on every render
});
例
- 空の配列:
useEffect(() => {
//Runs only on the first render
}, []);
例
- propsまたはstate value:
useEffect(() => {
//Runs on the first render
//And any time any dependency value changes
}, [prop, state]);
したがって、この問題を修正するには、最初のレンダリングでのみこの効果を実行しましょう。
例:
最初のレンダリングでのみ効果を実行します。
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
}, []); // <- add empty brackets here
return <h1>I've rendered {count} times!</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);
例:
以下は、変数に依存するuseEffect
フックの例です。count
変数が更新されると、エフェクトが再度実行されます。
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Counter() {
const [count, setCount] = useState(0);
const [calculation, setCalculation] = useState(0);
useEffect(() => {
setCalculation(() => count * 2);
}, [count]); // <- add the count variable here
return (
<>
<p>Count: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>+</button>
<p>Calculation: {calculation}</p>
</>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);
複数の依存関係がある場合、それらはuseEffect依存関係配列に含まれなければいけません。
効果のクリーンアップ
一部のエフェクトでは、メモリリークを減らすためにクリーンアップが必要です。
不要になったタイムアウト、サブスクリプション、イベント リスナー、およびその他の効果は破棄する必要があります。
これを行うには、useEffect
フックの最後にreturn関数を含めます。
例:
useEffect
フックの終了時にタイマーをクリーンアップします。:
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
let timer = setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
return () => clearTimeout(timer)
}, []);
return <h1>I've rendered {count} times!</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);
注:タイマーをクリアするには、名前を付ける必要があります。
プログラミング学習を加速させる
プログラミングをプロの講師に教えてもらいませんか。