fix(components): [select] correctly display selected labels after options change#24036
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughgetOption in the select hook now reuses an existing selected entry’s currentLabel when no cached option is found; a Vitest was added to verify a selected option’s label persists across remote-search result updates. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/components/select/__tests__/select.test.ts (1)
4579-4631: Make timer cleanup exception-safe.If anything throws before Line 4630, fake timers remain active and may destabilize later tests. Wrap test body in
try/finallyand restore timers there.Proposed test-hardening diff
test('should preserve selected label when remote options change', async () => { vi.useFakeTimers() const wrapper = mount( @@ }, { attachTo: 'body' } ) - - const select = wrapper.findComponent({ name: 'ElSelect' }).vm - select.onInput({ target: { value: 'foo' } }) - vi.runAllTimers() - await nextTick() - getOptions()[0].click() - await nextTick() - expect(select.states.selected[0].currentLabel).toBe('Label foo-0') - - select.onInput({ target: { value: 'bar' } }) - vi.runAllTimers() - await nextTick() - - expect(select.states.selected[0].currentLabel).toBe('Label foo-0') - vi.useRealTimers() + try { + const select = wrapper.findComponent({ name: 'ElSelect' }).vm + select.onInput({ target: { value: 'foo' } }) + vi.runAllTimers() + await nextTick() + getOptions()[0].click() + await nextTick() + expect(select.states.selected[0].currentLabel).toBe('Label foo-0') + + select.onInput({ target: { value: 'bar' } }) + vi.runAllTimers() + await nextTick() + + expect(select.states.selected[0].currentLabel).toBe('Label foo-0') + } finally { + wrapper.unmount() + vi.useRealTimers() + } })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/select/__tests__/select.test.ts` around lines 4579 - 4631, The test enables fake timers with vi.useFakeTimers() but only restores them at the end, so if something throws the fake timers remain active; wrap the test body (after vi.useFakeTimers()) in a try { ... } finally { vi.useRealTimers() } block so that vi.useRealTimers() is always called (ensure the existing interactions with wrapper, select.vm, select.onInput, vi.runAllTimers(), nextTick, getOptions(), etc. remain inside the try).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/components/select/__tests__/select.test.ts`:
- Around line 4579-4631: The test enables fake timers with vi.useFakeTimers()
but only restores them at the end, so if something throws the fake timers remain
active; wrap the test body (after vi.useFakeTimers()) in a try { ... } finally {
vi.useRealTimers() } block so that vi.useRealTimers() is always called (ensure
the existing interactions with wrapper, select.vm, select.onInput,
vi.runAllTimers(), nextTick, getOptions(), etc. remain inside the try).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: dc5f75c1-7e64-4920-945d-12aa105be33a
📒 Files selected for processing (2)
packages/components/select/__tests__/select.test.tspackages/components/select/src/useSelect.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c094f37b38
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
🧪 Playground Preview: https://element-plus.run/?pr=24036 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d14dc2b555
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
keeplearning66
left a comment
There was a problem hiding this comment.
The newly added test case seems to pass even before the changes. 🤔

Fixes selected items showing raw value instead of label when remote search
options change.
Root cause
The template renders options with
:key="index", so Vue reuses optioncomponents instead of destroying/recreating them. When props update:
cachedOptionsretains stale keys pointing to VMs whosevaluehas changedvm.value— no match → falls back to raw valueFix
In getOption, before the raw-value fallback, check
states.selectedfora previously resolved label.
issue
fix #23624
Summary by CodeRabbit
Bug Fixes
Tests