Skip to content

会場フォームのフックを早期 return の前に揃える (#372) - #481

Merged
kojira merged 1 commit into
mainfrom
fix/venue-form-hooks-372
Sep 4, 2026
Merged

会場フォームのフックを早期 return の前に揃える (#372)#481
kojira merged 1 commit into
mainfrom
fix/venue-form-hooks-372

Conversation

@kojira

@kojira kojira commented Sep 4, 2026

Copy link
Copy Markdown
Member

再現

/venues/:id/edit を、編集権限のない会場に対して開く。取得が終わるまでは
existing.data が undefined なので早期 return を通らずフォームが描画され、
応答が届いて「権限なし」と分かった瞬間に早期 return を通るようになる。
早期 return より後ろに useState が2つあったため2回目の描画でフックが減り、
React が Rendered fewer hooks than expected. This may be caused by an accidental early return statement. を投げる。利用者には「権限がありません」の案内ではなく
真っ白な画面(エラー境界まで巻き戻った画面)が出る。

直し方

imageFailedsavedIduseState を、他のフックと同じ関数の先頭へ移した
apps/web/src/pages/VenueFormPage.tsx:49-52)。memo で包んだり return の位置を
動かしたりはしていない。あわせて早期 return の手前に「ここから下でフックを足さない」
理由を書き残した(同 :74-75)。

このページの残りも同じ観点で見たが、他に早期 return の後ろのフックは無く、
既存値ロードの useEffect の依存 [existing.data, isEdit] も過不足なかった。

テストが噛むことの証明

apps/web/src/pages/VenueFormPage.hooks.test.tsx を追加した。読み込み中→権限なしの
差し替えを実際に描画する。修正を git stash で戻すと落ちる:

 FAIL  src/pages/VenueFormPage.hooks.test.tsx > 会場フォームのフックの順序 (#372) > 読み込み中から権限なしに変わってもフックの数が変わらない
AssertionError: expected [Function] to not throw an error but 'Error: Rendered fewer hooks than expe…' was thrown

- Expected:
undefined

+ Received:
"Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement."

修正を戻すと 2 件中 1 件が落ち、戻すのをやめると 2 件とも通る。

掃いた範囲

apps/web/src の全 .tsx(256 本)を「コンポーネント本体の早期 return より後ろの
フック呼び出し」で掃いた。このファイル以外に該当は無し
入れ子のコールバック内の return、useState の遅延初期化内の return、
useEffect のクリーンアップ return は除外している。
ついでに「if/for/try の中の直接のフック呼び出し」も掃いたが、こちらも 0 件。

確認

対象 結果
pnpm -r typecheck 3 プロジェクトとも通過
web (vitest run) 96 ファイル / 882 テスト 通過
server (run-tests.mjs) 全 11 シャード / 1152 テスト 通過

触ったファイルはいずれも 800 行未満(本体 325 行、テスト 128 行)。

Closes #372

🤖 Generated with Claude Code

https://claude.ai/code/session_01SLkuubF8cHzSnnsv4fN9DA

VenueFormPage は「編集権限なし」の早期 return より後ろで useState を
2つ呼んでいた。/venues/:id/edit を開いた直後は会場の取得が終わって
いないので早期 return を通らず、応答が届いて権限なしと分かった瞬間に
通るようになる。その差し替えでフックの数が減るため、React が
"Rendered fewer hooks than expected" を投げて画面が真っ白になる。

imageFailed と savedId を他のフックと同じ位置(関数の先頭)へ移し、
早期 return の手前に「ここから下でフックを足さない」と書き残した。
読み込み中→権限なしの差し替えを実際に描画するテストを足している
(移動前はこのテストが上記の Error で落ちる)。

apps/web/src 全体を同じ形(早期 return より後ろのフック呼び出し)で
掃いたが、他には無かった。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SLkuubF8cHzSnnsv4fN9DA
@kojira
kojira merged commit f2bd5b7 into main Sep 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VenueFormPage が早期 return の後ろで useState を呼んでいる(フックの順序が変わる)

1 participant