fix(#675): stop MarkdownEditor re-render loop blocking worktree URL updates#677
Merged
fix(#675): stop MarkdownEditor re-render loop blocking worktree URL updates#677
Conversation
- useFileTabs: SET_DIRTY short-circuits when target tab isDirty is unchanged, so upstream useReducer skips re-render and MarkdownEditor's onDirtyChange useEffect no longer feeds a loop. - WorktreeDetailRefactored: useCallback deps for file-panel handlers switched from [fileTabs] to [fileTabs.dispatch] to stabilise callback identity and stop MarkdownEditor from re-firing onDirtyChange every render. This unblocks router.push transitions when switching branches from the sidebar while a .md file is open. - Adds reducer tests covering same-value SET_DIRTY no-op (false→false, true→true, double-apply stable reference). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… acceptance, progress) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.mdファイル表示中にサイドバーから別ブランチを選択しても URL が更新されず画面遷移しないバグを修正。MarkdownEditor経由で発生していた無限 re-render ループがrouter.pushの App Router transition を starve させていた根本原因を A案 + B案 の多層防御で解消。Closes #675
Root Cause
useFileTabsの return が毎レンダー新オブジェクト参照を返すため、WorktreeDetailRefactoredの 4 つのuseCallback(deps=[fileTabs]) が毎レンダー再生成される。これがFilePanelContent→MarkdownEditorへ不安定 prop として伝播し、useEffect([isDirty, onDirtyChange])が毎レンダー発火 →dispatch({type:'SET_DIRTY'})→ reducer が同値でも新 state を返す → 上流再レンダー → ループ。高優先度 dispatch が積まれ続けるためrouter.pushが起動した低優先度 transition が starve し URL が commit されない。.md以外で再現しないのはMarkdownEditorだけがonDirtyChangeuseEffect を持つため。Changes
Fixed
src/components/worktree/WorktreeDetailRefactored.tsxのhandleLoadContent/handleLoadError/handleSetLoading/handleDirtyChangeのuseCallbackdeps を[fileTabs]から[fileTabs.dispatch]に変更。dispatchはuseReducer由来で identity stable なためコールバック identity が安定し、下流への不安定 prop 伝播が止まる。src/hooks/useFileTabs.tsのSET_DIRTYreducer で対象 tab のisDirtyが既に同値ならstateを同一参照のまま返す no-op 判定を追加。useReducerは state 参照が同一なら再レンダーをスキップするため、将来別経路で deps が崩れても SET_DIRTY 経由のループは構造的に防がれる多層防御。Added
tests/unit/hooks/useFileTabs.test.ts: SET_DIRTY 同値 no-op 検証 3 件 (false→false/true→true/ double-apply による state 参照安定性)。Out of Scope
同ファイル (
WorktreeDetailRefactored.tsx) 内の他 5 箇所 (L551/L570/L581/L875/L904) にも『fileTabs全体をdepsに置く』同種アンチパターンが残るが、Issue #675 の再現経路とは別系統のため別 Issue として起票予定。Test Results
Unit Tests
Lint & Type Check
Build
Manual Verification (recommended before merge)
.mdを開いた状態でサイドバーから Worktree B を選択 → URL が/worktrees/Bに変わる.yaml/.ymlでも同様に遷移する (MarkdownEditorを経由する全拡張子で同じ構造のため).png/.json/.pdfで回帰なし (元々再現しないので挙動維持のはず)Checklist
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com