ロードマップ
React 16.8 で追加された機能であるReactのHooks
について書いてあります。
書きながら学ぶ React Hooks 入門シリーズとして書き下ろしました。
はじめに
Reactの組み込みフックであるとuseRef
の説明をします。
useEffect とは
コンポーネントに副作用を追加するフックです。
副作用とは以下の4つです。
- Reactから生成されたDOMの変更
- APIとの通信
- 非同期処理
- console.log
副作用が分かりにくいので、もっと噛み砕いて言えば
useEffect
は、コンポネントのレンダリング後か、アンマウント(コンポネントをDOMから削除)後に、なにかの処理を実行させたいときに使うといいやつ。
って覚えとけばいいと思います。
構文
useEffect(副作用, [deps])
引数の渡し方によって、副作用が実行されるタイミングは異なります。
引数の渡し方によって、副作用が実行されるタイミング...1(毎回)
これもちゃんと動作します。
useEffect(副作用)
これは毎回のレンダリング後に副作用が実行されます。
引数の渡し方によって、副作用が実行されるタイミング...2([deps]更新時)
これは、例えば カウントアプリでボタンクリック時に state
(count) が更新された時、useEffect 内が実行されます。
useEffect(()=>{
setCount((count) => count + 1)
}, [count])
引数の渡し方によって、副作用が実行されるタイミング...3(最初の1回だけ)
第1引数をからの配列にすると、コンポネントがレンダリングされた後に1回だけ実行されます。
useEffect(()=>{
console.log('completed render')
}, [])
クリーンナップ関数
クリーンナップ関数は以下のタイミングで実行されます。
- コンポネントのアンマウント時
- 副作用の再実行時
useEffect(() => {
console.log('completed render');
const cleanup = () => {
console.log('cleanup!');
};
return cleanup;
}, []);
活用例
useEffectを使って、外部APIと通信し、非同期処理をさせて表示させましょう。
APIは、ダミーの顔写真付きユーザー情報をJSONで取得できるする「Random User Generator」を使います。
最初の1回だけしかレンダリングされない バージョン
サンプルコード
import React, { useState, useEffect } from "react";
const fetchUser = async () => {
const response = await fetch("https://api.randomuser.me/");
const data = await response.json();
const [user] = data.results;
return user;
};
const App = () => {
const [user, setUser] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser().then((userData) => {
setUser(userData);
setLoading(false);
});
}, []);
return (
<div className="App">
{loading ? (
<p>loading...</p>
) : (
<>
<p>
{user.name.first} {user.name.last} from: {user.location.country}
</p>
<img src={user.picture.large} alt={user.name.first} />
</>
)}
</div>
);
};
export default App;
useEffectの依存配列に何か指定したバージョン
依存配列に count
state を指定した場合
これは、Next User
ボタンクリック時に state (count) が更新されたことをフックして、useEffect 内が再度実行されます。
サンプルコード
import React, { useState, useEffect } from "react";
const fetchUser = async () => {
const response = await fetch("https://api.randomuser.me/");
const data = await response.json();
const [user] = data.results;
return user;
};
const App = () => {
const [user, setUser] = useState({});
const [loading, setLoading] = useState(true);
const [count, setCount] = useState(1);
useEffect(() => {
fetchUser().then((userData) => {
setUser(userData);
setLoading(false);
});
}, [count]);
const updateUser = () => setCount((currentCount) => currentCount + 1);
return (
<div className="App">
{loading ? (
<p>loading...</p>
) : (
<>
<p>Entry Number: {count}</p>
<p>
{user.name.first} {user.name.last} from: {user.location.country}
</p>
<img src={user.picture.large} alt={user.name.first} />
</>
)}
<div>
<button onClick={updateUser}>Next User</button>
</div>
</div>
);
};
export default App;
useEffectの依存配列に何も指定しないバージョン
まず以下は無限ループを繰り返す危険なコードです。
下記のサンプルコードの場合、APIをリクエストして、user
stateが書き換わったことをトリガーにして、またAPIをリクエストして、user
stateが書き換わることを繰り返します。
サンプルコード
import React, { useState, useEffect } from "react";
const fetchUser = async () => {
const response = await fetch("https://api.randomuser.me/");
const data = await response.json();
const [user] = data.results;
return user;
};
const App = () => {
const [user, setUser] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser().then((userData) => {
setUser(userData);
setLoading(false);
});
});
return (
<div className="App">
{loading ? (
<p>loading...</p>
) : (
<>
<p>
{user.name.first} {user.name.last} from: {user.location.country}
</p>
<img src={user.picture.large} alt={user.name.first} />
</>
)}
</div>
);
};
export default App;
さいごに
useEffect
にはまだまだ奥が深い部分があります。もっと詳細に書こうと思いましたが、別の機会にします。
詳しくは、以下の記事を参考にしてください。
僕もまた書きます。
今回は以上です。
参考