useEffectについて知る
副作用とは何か?
通常のプルグラム処理は引数を与えられて、返却値を返します。
例えば以下のCaluculateTax
関数は価格を引数に設定すると、税額を返します。CaluculateTax
関数は外部に何も影響を与えることはありません。
function CalculateTax(price) {
return price * 0.1
}
副作用とは関数の外に影響を与えてしまうことです。DOMを更新したり、APIを実行したり、console.log
を出力したり、ファイルに出力したり、状態を更新したりする処理が該当します。
クラスコンポーネントのライフサイクルメソッドとは何か?
クラスコンポーネントでは①componentDidMount
、②componentDidUpdate
、③componentWillUnmount
というライフサイクルメソッドがあります。componentDidMount
はコンポーネントが生成されるときに1回のみ実行される処理を記述します。(例えば初回のAPI実行でデータを取得する処理など)componentDidUpdate
はコンポーネントが更新されるときに実行される処理を記述します。componentWillUnmount
はコンポーネントが破棄されるときに実行される処理を記述します。(例えばAPIの通信切断など)
useEffect とは何か?
useEffectは副作用を実行できる、クラスコンポーネントのライフサイクルメソッドに相当するReact hooksです。
useEffectの第一引数には実行する処理を、第二引数には依存配列を指定します。依存配列に特定の変数を指定した場合はその変数が更新された場合に実行され、空配列を指定した場合は初回のみ実行されます。
useEffect(() => {
// 実行する処理
}, 依存配列);
クラスコンポーネントでの従来の実装
まず、クラスコンポーネントでの実装を見てみます。
以下の例はクラスコンポーネントでdocument.title
を副作用で変更している例です。
import React, { Component } from "react";
class ClassComponentWithoutUseEffect extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
componentDidMount() {
document.title = `Current count is ${this.state.count} !`;
}
componentDidUpdate() {
document.title = `Current count is ${this.state.count} !`;
}
render() {
return (
<div>
<p>current count is {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Count UP
</button>
</div>
);
}
}
export default ClassComponentWithoutUseEffect;
useEffectを使用しての実装
上記処理を関数コンポーネントで書き換えた例です。クラスコンポーネントでの実装と比べてかなり行数が少なくなったことがわかると思います。
import React, { Component, useEffect, useState } from "react";
function FuncComponentWithUseEffect() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Current count is ${count} !`;
}, [count]);
return (
<div>
<p>current count is {count}</p>
<button onClick={() => setCount(count + 1)}>Count UP</button>
</div>
);
}
export default FuncComponentWithUseEffect;
useEffectでAPI実行を実装してみた
useEffect内でAPI実行を行う処理を実装してみました。APIのリクエストパラメータとしてTodoのIDをテキストフィールドから設定します。エラー時、ローディング時には専用の表示に切り替わります。
import React, { useEffect, useState } from "react";
function SearchTodo() {
const [error, setError] = useState(null);
const [todoId, setTodoId] = useState(1);
const [todo, setTodo] = useState({});
const [loading, setLoading] = useState(true);
const fetchTodo = async () => {
await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`)
.then(res => res.json())
.then(
(result) => {
setTodo(result);
setLoading(false);
},
(error) => {
setError(error.message);
setLoading(false);
}
);
};
useEffect(() => {
fetchTodo();
}, [todoId]);
return (
<div>
<input
type="text"
value={todoId}
onChange={(e) => setTodoId(e.target.value)}
/>
{error ? (
<div style={{ color: "red" }}>Error: {error}</div>
) : loading ? (
<h1>loading...</h1>
) : (
<div>{todo.title}</div>
)}
</div>
);
}
export default SearchTodo;