R初めてのReact

Day 4useEffect と データ取得 — 外の世界とつながる

副作用とuseEffect — 描画のあとに動く処理

「画面を描くこと以外の処理」をどこに書くか。useEffectと依存配列の意味を、たとえで理解する。

副作用ってなに?

Reactのコンポーネントは、本来は「今の状態(state)を受け取って、画面(JSX)を返すだけ」の関数です。入力が同じなら出力も同じ、という素直な計算に近いものです。ところが実際のアプリでは、それだけでは足りません。サーバーにデータを取りに行ったり、タイマーを動かしたり、外部のイベントを購読したり、といった「画面を描くこと以外の処理」が必要になります。この、描画の外側で世界に触れる処理のことを副作用(effect)と呼びます。

たとえるなら、コンポーネントの本体は「注文票を見て料理の見た目を絵に描く」係です。でも実際にはお客さんの人数をお店に電話で問い合わせたり、タイマーをセットして焼き加減を測ったりもしたい。この「電話をかける」「タイマーをセットする」といった、絵を描くのとは別の外向きの行動が副作用です。これを絵を描いている最中にやると手が止まってしまうので、描き終わったあとにまとめてやろう、というのがuseEffectの発想です。

なぜ描画とは分けるのか

Reactは同じstateなら同じ画面になることを前提に、再描画のたびにコンポーネント関数を何度も呼び出します。もし関数の本体で直接fetchやタイマーを動かすと、描画のたびに通信が走ったり、動作が予測できなくなります。だから副作用は本体から追い出し、useEffectという専用の置き場所にまとめます。

useEffectの形

useEffectは「描画が終わったあとに、この処理を動かして」とReactにお願いする道具です。第一引数に動かしたい関数を、第二引数に依存配列(deps)という配列を渡します。この依存配列が、いつ再実行するかを決める心臓部です。

TSX
import { useEffect, useState } from "react";

function Clock() {
  const [now, setNow] = useState(new Date());

  useEffect(() => {
    // 描画のあとに1秒ごとに現在時刻を更新する副作用
    const id = setInterval(() => setNow(new Date()), 1000);
    // 後片付け: このコンポーネントが消えるときにタイマーを止める
    return () => clearInterval(id);
  }, []);

  return <p>{now.toLocaleTimeString()}</p>;
}
useEffect(処理, 依存配列) の形。returnした関数は後片付けに使われる

依存配列 = いつ再実行するか

依存配列は「この中の値が前回と変わっていたら、もう一度副作用を実行してね」という指示リストです。Reactは再描画のたびに、配列の中身を前回と見比べます。何か変わっていれば副作用を動かし直し、すべて同じなら何もしません。だから配列に何を入れるかで、実行のタイミングが決まります。

  • 依存配列なし → 毎回の描画のあとに実行される(使いどころは少ない)
  • 空の配列 [] → 最初の描画のあと一度だけ実行される
  • [url] のように値を入れる → その値が変わったときだけ再実行される
TSX
// userId が変わるたびに、そのユーザーのデータを取り直す
useEffect(() => {
  fetch(`/api/users/${userId}`)
    .then((r) => r.json())
    .then(setUser);
}, [userId]);
[userId] を依存に入れると、別のユーザーに切り替わったとき自動で取り直す

依存の入れ忘れに注意

副作用の中で使っている値(上の例のuserIdなど)は、原則すべて依存配列に入れます。入れ忘れると、値が変わっても副作用が古い値のまま動き続け、「別のユーザーを選んだのに前の人のデータが出る」といったバグになります。逆に空配列 [] は「本当に初回だけでいい」と確信できるときにだけ使いましょう。

AIにはこう聞く

「このuseEffectの依存配列はこれで正しいですか?中で使っている値を全部挙げて、入れ忘れがないか指摘してください」と聞くと、依存の抜けを洗い出せます。AIが生成したuseEffectも、依存配列だけは自分の目で確認するクセをつけましょう。

読んでも腑に落ちない所は、AIに噛み砕いてもらいましょう。

理解度チェック

答えられれば、面接で説明できる状態に一歩近づきます。

Q1

useEffectの第二引数に空の配列 [] を渡すと、副作用はいつ実行されますか?

Q2

副作用(effect)の説明として最も適切なものは?

Q3

依存配列(deps)が何を決めているのか、たとえを交えて説明してください。

記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。