useEffectはよく使うhookですが、正しく使えているのが不安になったので、調べてみました。
useEffectについて
React16.8でフックという機能が追加され、useEffectというフックが使えるようになった。
この新機能によって、stateの機能などが関数コンポーネントでも使えるようになった。
useEffectフックはcomponentDidMountとcomponentDidUpdateとcomponentWillUnmountがまとまったもの
useEffectがやっていること
React がコンポーネントをレンダーする際にReactは副作用を覚えておき、DOMを更新した後に呼び出す。
- レンダー後に何かの処理をしないといけない、ということをReactに伝える
- Reactは渡された関数を覚える
- DOMの更新の後にその関数を呼び出す
「レンダーの後」に副作用は起こる
デフォルトでは、副作用関数は初回のレンダー時および毎回の更新時に呼び出される。
Reactは、副作用が実行される時点ではDOMが正しく更新され終わっていることを保証する。
レンダーごとに渡される関数は異なる
再レンダーごとに、React は違う副作用関数をスケジュールし、前のものを置き換えるため、古い値を参照してしまう心配なしに関数を実行することができる。
副作用は特定の1回のレンダーと結びついている。
クリーンアップを必要としない副作用
ReactがDOMを更新した後で追加のコードを実行したい時。例えば、
- ネットワークリクエストの送信
- 手動でのDOM改変
- ログの記録
コードが実行されたあとすぐにその関数のことを忘れても構わないため。(継続しない処理)
クリーンアップを有する副作用
外部のデータソースへの購読をセットアップしたい時などは、メモリリークが発生しないようにクリーンアップが必要。
その他マウント中に継続するような処理setIntervalなど。
クリーンアップの例
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
クリーンアップ処理の特徴
useEffect関数内に書く
クリーンアップ用の副作用は必要なく、useEffect内に一緒に書くことができる。
実行されるタイミング
- コンポーネントのアンマウント時
- 毎回のレンダー時(副作用関数が実行される前)
スキップによるパフォーマンスの改善
副作用のクリーンアップと関数の処理をレンダーごとに毎回行うとパフォーマンスの問題を引き起こす可能性がある。
第2引数に配列を渡すことで、その配列内の要素に変更がない場合は処理をスキップする。
配列に複数の要素がある場合は一つでも変わっている場合は処理を実行する。
例では、第2引数に[count]を渡している。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
第2引数に[]空配列を渡すと、副作用内ではpropsとStateは初期値のままになる。propsやStateに依存しないので、再実行は発生せず、レンダー時に実行されるのみとなる。
参考