おはこんばんちは、@ちーずです。
アドベントカレンダー3日目にして、すでに毎日記事を書くことの大変さを実感してます
本日のテーマはuseEffect
です!
useEffect
とは
useEffect
とは、関数コンポーネントで副作用を実行するためのhookです。
...と言われてもReactにおける副作用ってなんぞ?ってなりますよね。
Reactのコンポーネントの世界において、副作用はコンポーネントのライフサイクルのことを指します。(多分)
▼ コンポーネントのライフサイクル
(クラスコンポーネントにおけるライフサイクルメソッド名を添えて)
- マウントされる (
componentDidMount
) - 更新される (
componentDidUpdate
) - アンマウントされる (
componentWillUnmount
)
書き方
// 第一引数: コールバック関数(任意でcleanup関数を返す)
// 第二引数: 配列
useEffect(() => {
// 実行したい処理
return () => {
// cleanupの処理
}
}, [input])
毎回のレンダリング後に実行
第二引数を空にすることで、レンダリング毎に実行されます。
useEffect(() => {
// 処理
})
コンポーネントはstateやpropsなどに変更がある度にレンダリングされてしまいます。
そのため予期しないケースでuseEffectが実行されてしまう可能性があるため、第2引数を空にすることは危険です。
初回のレンダリング後に実行
第二引数をの配列の中身を空にすると、
初回のレンダリング後のみ実行されます。
useEffect(() => {
// 処理
}, [])
指定した値に変化があった時に実行
第二引数の配列に値を設定することで、
その値に変更がある度に実行することができます。
useEffect(() => {
// 処理
}, [value]))
第二引数は、基本的にはlint(react-hooks/exhaustive-deps)とエディタの機能に力を借りて
自動で設定すればokです!
useEffect
の活用術
ステートの変更を監視して実行
いっちばん基本的な使われ方ですが、
ステートの変更を監視して処理を実行することができます。
▼ 例
useEffect(() => {
if (count < 10) return;
console.log('countがもう10以上やで')
}, [count]);
条件に当てはまらない、実行したくない場合は早期リターンしてあげることをおすすめします。
外側から実行条件を渡して汎用化
処理は同様だけど、処理を実行させる条件が異なるケースがあると思います。
そのような場合は、useEffect
を別コンポーネントもしくはカスタムフックとして別途作成し、
条件をprops
や引数
で渡せるようにします。
▼ よく使うprops
や`引数
-
skip
: 処理をスキップするか - falseの時のみ実行させる -
prepared
: 準備ができたか - trueの時のみ実行させる -
onMounted / onUnMount
: マウント または アンマウント で実行させる
▼ 例: カスタムフックを使用した例
export const useCustomHook = (skip: false) => {
useEffect(() => {
if (skip) return;
// 共通化したい処理
}, [skip])
}
カスタムフックとはなんぞ?と思った人にはおすすめの記事があります。(宣伝)
※ 活用術に関しては、発見次第今後追記していく予定です!
useEffect
の注意事項
一見便利なuseEffect
ですが、使い方を誤ると予期せぬバグを産んでしまいがちです。
そのため、どのようなバグが発生しがちか、どのように対処すべきかも理解した上で使えるとより良いです。
無限ループに気をつける
useEffect
は特に無限ループに陥りがちです。
ありがちなのが、ステートの更新をuseEffect
の中で行っているが、
そのuseEffect
はステートが更新されたら再度実行されるようになっていて、
更新 → 実行 をひたすら繰り返してしまうことです。
const [count, setCount] = useState(0)
useEffect(() => {
setCounter(prev => prev + 1)
}, [count])
useEffect
は、ちゃんと無限ループがどのようなケースで発生しうるかを理解して使うことが大事です。
▼ 参考
メモリリーク対策をしっかりする
メモリリークとは、実行中のプログラムが割当てたメモリ領域を解放し忘れることで起きるバグです。
useEffect
を使った場合、非同期の処理においてResponse返ってくるよりも先にコンポーネントが消失した場合に発生しうります。
その場合は、ちゃんとコンポーネントがマウントされた状態であることを判定した上で実行しましょう。
▼ 例
useEffect(() => {
let isMounted = true;
// promise() - Promiseを返す何かしらの処理
promise().then((n) => {
isMounted && setState(n);
});
// アンマウント時にmountedをfalseに変更
return () => {
isMounted = false;
};
}
▼ 参考
イベントを追加した時は、クリーンアップ時に削除する
addEventListener
をuseEffect
内で使う場合、
useEffect
が走る度にイベントが追加され続けてしまいます。
そのため、クリーンアップ時にイベントを一度削除するよう制御しましょう。
▼ 例
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [handleResize]);
以上、「useEffect
の基本的な使い方と活用術」でした!
明日はuseRef
の基本的な使い方と活用術に関してです!
お楽しみに