Skip to content

fix(ui): Remove dead dirs state and unused hook parameter from InputPrompt#2891

Merged
tanzhenxin merged 3 commits intoQwenLM:mainfrom
chinesepowered:useeffect-render
Apr 8, 2026
Merged

fix(ui): Remove dead dirs state and unused hook parameter from InputPrompt#2891
tanzhenxin merged 3 commits intoQwenLM:mainfrom
chinesepowered:useeffect-render

Conversation

@chinesepowered
Copy link
Copy Markdown
Contributor

@chinesepowered chinesepowered commented Apr 4, 2026

Remove dead dirs state and unused hook parameter from InputPrompt.

TLDR

The dirs parameter passed to useCommandCompletion() was never referenced inside the hook body — only declared on line 42. The dirs state, its sync useEffect, and the parameter itself were all dead code. This PR removes them entirely instead of optimizing the sync effect.

Screenshots / Video Demo

N/A — no user-facing change. Pure dead-code removal.

Dive Deeper

Thanks to the reviewer for catching this. The original PR tried to fix an effect that was running on every render (because getDirectories() returns a new array each call). The first attempted fix used a serialized dependency key to stabilize the dep. The reviewer correctly pointed out that the whole thing was pointless:

The dirs parameter passed to useCommandCompletion() is never actually read inside that hook — it's declared on line 42 but not referenced anywhere in the function body. This makes the dirs state and its syncing effect dead code.

Verified: grep dirs in useCommandCompletion.tsx returns only the parameter declaration.

So instead of optimizing dead code, this PR deletes it:

  1. useCommandCompletion.tsx — removed the dirs: readonly string[] parameter
  2. InputPrompt.tsx — removed the dirs state, the currentDirs/dirKey locals, the sync useEffect, and the dirs argument at the call site
  3. useCommandCompletion.test.ts — removed testDirs local and the testDirs, argument from all 17 call sites
  4. InputPrompt.test.tsx — removed the ['/test/project/src'] second argument from all toHaveBeenCalledWith assertions

Net: 40 lines deleted, 0 lines added. No behavior change — the hook never used the value.

Modified files:

  • packages/cli/src/ui/components/InputPrompt.tsx
  • packages/cli/src/ui/components/InputPrompt.test.tsx
  • packages/cli/src/ui/hooks/useCommandCompletion.tsx
  • packages/cli/src/ui/hooks/useCommandCompletion.test.ts

Reviewer Test Plan

  1. Run useCommandCompletion tests: pnpm --filter=@qwen-code/qwen-code exec npx vitest run src/ui/hooks/useCommandCompletion.test.ts — 17 pass
  2. Run InputPrompt tests: pnpm --filter=@qwen-code/qwen-code exec npx vitest run src/ui/components/InputPrompt.test.tsx — 103 pass, 2 skipped
  3. Grep the repo for any remaining references to the removed parameter: grep -r "useCommandCompletion" packages/cli/src — all call sites match the new 8-arg signature
  4. Manual smoke: add/remove working directories with /add-dir during a session — prompt still works correctly (directory state lives in config.getWorkspaceContext(), not in the removed local state)

Testing Matrix

macOS Windows Linux
npm run ? pass ?
npx ? ? ?
Docker ? ? ?
Podman ? - -
Seatbelt ? - -

Linked issues / bugs

Linked to bug report issue for useEffect running every render in InputPrompt. The reviewer correctly identified that the root cause was dead code, not a missing optimization.

getDirectories() returns a new array reference each call, causing the
useEffect dependency check to fail on every render. Move the call
inside the effect body and use stable dependencies [config, dirs] so
the effect only re-runs when they actually change.
Copy link
Copy Markdown
Collaborator

@tanzhenxin tanzhenxin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: fix(ui): prevent useEffect from running every render in InputPrompt

Overview

Fixes a performance issue where a useEffect in InputPrompt fired on every render because getDirectories() returns Array.from(this.directories) — a new array reference each call — placed directly in the dependency array. The fix moves the call inside the effect body and uses [config, dirs] as dependencies.


Critical Issues

1. Effect no longer detects external directory changes (Medium-High)

Both config (likely a stable singleton) and dirs (local state, only changes via setDirs inside this effect) are stable references. After the initial render, the effect has no external trigger. If directories are added/removed externally (e.g., via /add-dir, which mutates WorkspaceContext in place at directoryCommand.tsx:135), neither config nor dirs changes identity, so the effect never re-runs.

The old code handled this correctly (albeit wastefully) — by calling getDirectories() during render and putting the result in deps.

Recommendations:

  1. Subscribe to workspaceContext.onDirectoriesChanged() if such an API exists.
  2. Or use a serialized dep key: const dirKey = config.getWorkspaceContext().getDirectories().join('\0') and use [dirKey] as the dependency — still calls getDirectories() every render but the string comparison is cheap and stable.
  3. Or remove the dirs state entirely if nothing actually consumes it.

2. Self-triggering cycle (Low)

dirs in the dependency array means every real directory change causes the effect to run twice — once to detect, once after setDirs updates. The inner comparison stops infinite loops, but it's unnecessary work.


Positives

  • Correct diagnosis of the Array.from() new-reference problem.
  • Improved comparison: old code only compared dirs.length; new code does element-wise .every().
  • Clean, minimal diff.

Verdict

REQUEST_CHANGES — The fix solves the performance problem but introduces a functional regression where directory changes after mount go undetected. The effect's dependency array needs a mechanism to observe external directory mutations.

Move from [config, dirs] deps (both stable refs that miss external
changes) to a dirKey string (join of current directories). This
preserves the perf fix (no new array ref in deps) while still
detecting directory additions/removals from /add-dir etc.
@chinesepowered
Copy link
Copy Markdown
Contributor Author

the original fix with [config, dirs] deps would miss external directory changes because both are
stable references. The new approach:

  • currentDirs — called every render (cheap Array.from() call)
  • dirKey = currentDirs.join('\0') — serialized string that's stable when dirs haven't changed, but updates when they do
  • useEffect(..., [dirKey]) — only fires when directories actually change

This keeps the perf fix (no new array ref in deps) while correctly detecting external mutations from /add-dir and similar commands.

Copy link
Copy Markdown
Collaborator

@tanzhenxin tanzhenxin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

The dirs parameter passed to useCommandCompletion() is never actually read inside that hook — it's declared on line 42 but not referenced anywhere in the function body. This makes the dirs state and its syncing effect dead code.

Rather than optimizing the sync, consider removing the dirs state and the unused parameter entirely.

The dirs parameter passed to useCommandCompletion() was never read
inside that hook, making the dirs state and sync effect in InputPrompt
dead code. Remove the parameter, the state, the effect, and all test
call-site args.
@chinesepowered chinesepowered changed the title fix(ui): prevent useEffect from running every render in InputPrompt fix(ui): Remove dead dirs state and unused hook parameter from InputPrompt Apr 7, 2026
@chinesepowered
Copy link
Copy Markdown
Contributor Author

Review

The dirs parameter passed to useCommandCompletion() is never actually read inside that hook — it's declared on line 42 but not referenced anywhere in the function body. This makes the dirs state and its syncing effect dead code.

Rather than optimizing the sync, consider removing the dirs state and the unused parameter entirely.

Oh wow, good catch! Updated PR description and code. Thanks!

Copy link
Copy Markdown
Collaborator

@tanzhenxin tanzhenxin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean dead-code removal. The dirs parameter in useCommandCompletion() was declared but never read — removing it along with the state and sync effect in InputPrompt is the right call. LGTM.

@tanzhenxin tanzhenxin merged commit 5a5a175 into QwenLM:main Apr 8, 2026
14 checks 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.

2 participants