コース一覧
フロントデバッグ道場 呪われたウィジェット供養の10問
無限ループを止めろ

フロントデバッグ道場 呪われたウィジェット供養の10問

React で書かれた壊れたウィジェットを、自分の手で直していく演習専用コースです。解説はありません。症状と完成条件だけを読み、原因を突き止めて直します。手が止まったときのために、方針・調べ方・部分解の3段階のヒントを各問に用意しています。他の道場と違い、白紙からではなく「動いているように見えて壊れているコード」から始まります。関数名のタイポ、フォームの既定動作、key 忘れ、プロパティ名の取り違えといった見つけやすい呪いから始まり、state の直接変更、useEffect の無限ループ、set 直後の古い値、非同期応答の追い越しといった再現しにくい呪いへ進み、最後は5つの呪いが同時にかかったウィジェットを1つずつ払います。1問15分から45分、全10問で約4.5時間です。React入門を終えて「エラーが出ると手が止まる」と感じている方に向いています。

1
初段
01. ボタンが反応しない15分
02. 送信すると消える15分
03. 黄色い警告を消せ20分
04. undefined が出る20分
2
中段
01. 数字が増えない25分
02. 無限ループを止めろ25分
03. 一つ前の値が表示される30分
04. 検索結果が入れ替わる30分
3
師範
01. 赤いエラーを読んで直せ35分
02. 総合 5つの呪いを全部払え45分

無限ループを止めろ

中段 / 目安 25分

琥珀さんが自分のノートPCを開いて、耳を近づけてきます。「新着ウィジェットを開くとPCのファンが唸るんだ。ネットワークタブを見て……APIが毎秒何十回も呼ばれてる!」。

画面そのものは正常に見えます。新着のお知らせは3件ちゃんと並んでいます。ところが className が calls の段落に出ている通信回数が、放っておくだけでどんどん増えていきます。誰も何も押していないのに、です。

これは useEffect の依存配列 の呪いです。useEffect の第2引数に渡した配列は「この中身が前回と変わっていたら、もう一度実行する」という指定です。React はその中身を1つずつ、Object.is に近い同一性で見比べます。数や文字なら中身が同じなら同じものとして扱われますが、オブジェクトや配列は違います。

JavaScript (JSX)

{ sort: "new" } === { sort: "new" } // false

見た目がまったく同じでも、別々に作られたオブジェクトは別物です。そしてコンポーネントの本体で const options = { sort: "new" }; と書くと、描画のたびに新しいオブジェクトが作られます。つまり依存配列の中身は毎回「変わった」と判定されます。

ここに、効果の中で state を更新する処理が入ると輪が閉じます。効果が走る、state が変わる、描き直される、新しいオブジェクトができる、依存が変わったと判定される、効果がまた走る。止まりません。これが無限ループです。

今回のコードには前任者が入れた非常ブレーキが付いていて、20回で止まるようになっています。おかげでブラウザは落ちませんが、20回呼ばれている時点で立派な呪いです。

直し方を考えるときの問いは1つです。この効果は、本当に何かが変われば呼び直すべきものなのか。 今回は「開いたときに1回だけお知らせを取ってくる」だけなので、変化に反応する必要はまったくありません。依存が空なら、効果は最初の1回しか走りません。

もし本当に何かに反応させたい場合でも、渡すのはオブジェクトそのものではなく、その中の文字や数にします。[options] ではなく [options.sort] です。同一性ではなく値で比べられるものだけを依存に置く、と覚えておいてください。

完成条件

  • 画面を開いてしばらく置いても、className が calls の段落が「通信 1 回」のまま増えない
  • 新着のお知らせは今までどおり className が item の要素として3件並ぶ
  • useEffect の依存配列に、描画のたびに作り直されるオブジェクトを渡さない
  • className が settled の段落に出る「1.5秒後の通信回数」が 1 になる

進め方

まず、効果の先頭に console.log を1行入れて、何回走っているかを自分の目で数えてください。1回で止まらないことが確認できたら、次は「なぜもう一度走ったのか」です。依存配列に並んでいるものを1つずつ、それは前回と同じものか と問い直します。

画面には前任者が付けた見張りが2つ残っています。呼びすぎを止める LIMIT と、開いて1.5秒たった時点の通信回数を書き留める settled です。どちらも呪いではなく安全装置なので、触らずに残してください。ブレーキを外して呪いが消えたように見せるのは供養ではなく、隠蔽です。

ヒントは3段階で、①方針、②使う構文、③部分解の順に出ます。1つも開けずに通せたら、その問題は自分のものです。

供養が済むと、琥珀さんは耳をすませてこう言います。「ファンが……静かに……(会社が救われた音)」。

ヒント

前のヒントを開くと次が開きます。開かずに解けると未開封クリアです

①方針 症状は「効果が何度も走っている」ことです。まず効果の先頭に console.log を入れて回数を数え、次に依存配列に並んでいるものが前回と同じものかどうかを1つずつ問い直してください。オブジェクトは中身が同じでも別物として扱われます。

ヒント 2ヒント 1 を開くと読めます
ヒント 3ヒント 2 を開くと読めます
App.jsx
mock-api.json
styles.css
プレビュー

できているか

  • 新着のお知らせが3件並んでいる
  • 1.5秒たっても通信が1回で止まっている
  • 依存配列に作り直されるオブジェクトを渡していない
  • 非常ブレーキを外していない
エディタを読み込んでいます