Skip to content

feat: 添加手动控制选中值的功能到 Picker 组件#7024

Open
lzm0x219 wants to merge 2 commits intoant-design:masterfrom
lzm0x219:feature/picker-selectvalue
Open

feat: 添加手动控制选中值的功能到 Picker 组件#7024
lzm0x219 wants to merge 2 commits intoant-design:masterfrom
lzm0x219:feature/picker-selectvalue

Conversation

@lzm0x219
Copy link

@lzm0x219 lzm0x219 commented Jan 26, 2026

功能描述

Picker 组件添加了手动控制选中值的功能,通过新的 selectValue 属性允许用户以编程方式控制 Picker 组件中的临时选中值。

改动说明

核心功能

  • 新增 selectValue 属性:允许开发者通过传入此属性来手动控制 Picker 中的选中值,与 value 属性配合使用
  • 状态管理优化:将内部的 innerValue 改为使用 usePropsValue hook,更好地处理受控组件逻辑

修改的文件

  • picker.tsx

    • 添加 selectValue 到 PickerProps 类型定义
    • 使用 usePropsValue 替代 useState 来管理内部选中值
    • 优化导入顺序
  • demo1.tsx

    • 新增 RenderChildrenDemo2 演示组件,展示如何通过外部操作(点击标题)来设置选中值
    • 演示了在 onSelect 回调中更新 selectValue 的用法

使用示例

const [value, setValue] = useState<(string | null)[]>([])
const [selectValue, setSelectValue] = useState<(string | null)[]>([])

<Picker
  columns={basicColumns}
  value={value}
  selectValue={selectValue}
  onSelect={(val) => setSelectValue(val)}
  onConfirm={setValue}
>
  {/* children */}
</Picker>

类型兼容性

✅ 向后兼容 - selectValue 属性为可选,不影响现有代码


Summary by CodeRabbit

发布说明

  • 新功能
    • 新增演示示例,展示通过受控方式管理选择器的当前选中项并在界面中实时显示选择状态,包含手动控制和确认机制的交互演示。
    • 增强选择器的受控能力,支持外部传入选中值以便更灵活地管理和同步选择状态,提升与外部状态管理的兼容性。

✏️ Tip: You can customize this high-level summary in your review settings.

@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jan 26, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 26, 2026

Preview is ready

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @lzm0x219, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求为 Picker 组件引入了 selectValue 属性,旨在提供对组件临时选中值的编程控制能力。这一改动增强了组件的灵活性,允许外部逻辑更精细地管理其内部状态,与现有的 value 属性(用于确认值)形成互补。同时,内部状态管理已重构为使用 usePropsValue hook,以优化受控组件的实现方式。

Highlights

  • 新增 selectValue 属性: 为 Picker 组件引入了 selectValue 属性,允许以编程方式控制组件的临时选中值,与 value 属性协同工作。
  • 状态管理优化: 将 Picker 组件内部的 innerValue 状态管理从 useState 迁移到 usePropsValue hook,以更好地支持受控组件的行为。
  • 示例更新: 在 demo1.tsx 中添加了 SelectedDemo 示例,展示了如何利用 selectValue 属性进行外部控制和 onSelect 回调的用法。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

新增示例组件 SelectedDemo 展示通过 selectValue 临时控制选择值;Picker 增加公开属性 selectValue?: PickerValue[],并将内部 innerValue 的来源由 useState 切换为 usePropsValue,以支持外部驱动选择流程。

Changes

Cohort / File(s) 变更摘要
Demo 演示
src/components/picker/demos/demo1.tsx
新增 SelectedDemo 组件:维护独立的 value(确认值)与 selectValue(临时选择),标题点击预设选择,onSelect 更新 selectValueonConfirm 更新 value,并在 DemoBlock 中新增 “使用 selectValue 来控制选择值”。
Picker 实现与 API
src/components/picker/picker.tsx
PickerProps 中新增 selectValue?: PickerValue[]。将内部 innerValueuseState 改为 usePropsValue,使其可由 selectValue 控制;在打开时若 selectValue 未传则重置 innerValue,其余确认/选择逻辑保留。

Sequence Diagram(s)

sequenceDiagram
participant User
participant SelectedDemo
participant Picker

User->>SelectedDemo: 点击打开 Picker
SelectedDemo->>Picker: render with props(selectValue, onSelect, onConfirm)
Picker->>SelectedDemo: onSelect(tempVal)
SelectedDemo->>SelectedDemo: 更新 selectValue(临时显示)
User->>Picker: 确认选择
Picker->>SelectedDemo: onConfirm(finalVal)
SelectedDemo->>SelectedDemo: 将 finalVal 设为 value(持久)
Loading

Estimated code review effort

🎯 3 (中等) | ⏱️ ~20 minutes

诗篇

🐰 我是小兔来报到,
选项跳跳乐逍遥,
selectValue 暂且抱,
确认一刻成了宝,
胡萝卜庆祝咬一口 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 标题清晰准确地反映了主要变更内容——为 Picker 组件添加手动控制选中值的功能,与代码改动(新增 selectValue 属性和相关实现)完全对应。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot dosubot bot added the feature label Jan 26, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

本次 PR 旨在为 Picker 组件添加 selectValue 属性以实现对临时选中值的手动控制,这是一个很好的功能增强。代码实现上通过 usePropsValue 来统一处理受控与非受控逻辑,思路是正确的。但在目前的实现中,我发现了一些问题:

  1. 用于重置选择器状态的 useEffect 钩子没有考虑到 selectValue 受控的情况,这会导致外部传入的 selectValue 值被意外覆盖,属于比较严重的逻辑缺陷。
  2. onSelect 回调会在每次选择时被触发两次,这可能导致不符合预期的行为和性能问题。

我在代码中留下了具体的修改建议,希望能帮助你完善这个功能。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/picker/picker.tsx (2)

39-77: selectValue 属性未在 PickerProps 类型中声明。

代码在第 134 行使用了 props.selectValue,但 PickerProps 类型定义中缺少该属性声明,这会导致 TypeScript 类型错误。

🐛 建议修复
 export type PickerProps = {
   columns: PickerColumn[] | ((value: PickerValue[]) => PickerColumn[])
   value?: PickerValue[]
   defaultValue?: PickerValue[]
+  selectValue?: PickerValue[]
   loading?: boolean
   loadingContent?: ReactNode
   onSelect?: (value: PickerValue[], extend: PickerValueExtend) => void

133-158: onSelect 被重复调用两次。

存在逻辑冲突:

  1. usePropsValueonChange 回调(第 136-139 行)会在 setInnerValue 时触发 props.onSelect
  2. onChange 函数(第 156 行)在 visible 为 true 时也会调用 props.onSelect

这导致每次选择变更时 onSelect 被调用两次。

🐛 建议修复:移除 onChange 中的重复调用
     const [innerValue, setInnerValue] = usePropsValue({
       value: props.selectValue,
       defaultValue: value,
       onChange: val => {
         const extend = generateColumnsExtend(props.columns, val)
         props.onSelect?.(val, extend)
       },
     })
   
     useEffect(() => {
       if (innerValue !== value) {
         setInnerValue(value)
       }
     }, [visible])
     useEffect(() => {
       if (!visible) {
         setInnerValue(value)
       }
     }, [value])
 
     const onChange = useMemoizedFn((val, ext) => {
       setInnerValue(val)
-      if (visible) {
-        props.onSelect?.(val, ext)
-      }
     })

或者,如果需要保留原有的 onChange 调用逻辑,则应移除 usePropsValue 中的 onChange 回调:

🐛 备选方案:移除 usePropsValue 中的 onChange
     const [innerValue, setInnerValue] = usePropsValue({
       value: props.selectValue,
       defaultValue: value,
-      onChange: val => {
-        const extend = generateColumnsExtend(props.columns, val)
-        props.onSelect?.(val, extend)
-      },
     })
🧹 Nitpick comments (1)
src/components/picker/picker.tsx (1)

142-146: 数组比较使用引用相等性,需确认是否符合预期。

第 143 行 innerValue !== value 使用引用相等性比较两个数组。如果数组内容相同但引用不同,条件仍为 true。这在 React 中是常见做法,但请确认这是预期行为。

@lzm0x219 lzm0x219 force-pushed the feature/picker-selectvalue branch from 48a6f5f to a15adf3 Compare January 26, 2026 23:52
@lzm0x219
Copy link
Author

lzm0x219 commented Jan 26, 2026

@gemini-code-assist /gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

这个 PR 旨在为 Picker 组件添加 selectValue 属性以实现手动控制选中值,方向是正确的,并且使用了 usePropsValue 来处理受控逻辑,这很好。然而,一个关键问题是,现有的用于管理选择器内部状态的 useEffect 钩子没有相应更新,导致它们与新的受控属性 selectValue 产生冲突。这会导致在取消操作后重新打开选择器时行为不正确。

Comment on lines +128 to +131
const [innerValue, setInnerValue] = usePropsValue({
value: props.selectValue,
defaultValue: value,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

useState 改为 usePropsValue 是实现 selectValue 受控功能的正确方向。但这个改动与后面(133-137行)的 useEffect 产生了冲突。

原有的 useEffect 会在 visible 状态改变时,尝试将 innerValue 重置为 value。当 selectValue 属性被使用时,innerValue 由外部控制,这个 useEffect 会错误地覆盖外部传入的值,尤其是在取消后重新打开选择器时,会导致 selectValue 的值被忽略,并可能引起界面闪烁。

为了修复这个问题,需要修改 133-137 行的 useEffect,使其在 props.selectValue 被定义(即受控模式)时不执行重置逻辑。例如:

useEffect(() => {
  // 仅在非受控模式下,当 picker 打开时,重置 innerValue
  if (props.selectValue === undefined && visible) {
    if (innerValue !== value) {
      setInnerValue(value);
    }
  }
}, [visible, value, innerValue, props.selectValue]);

由于这个 useEffect 不在本次 diff 的修改范围内,我无法直接提供代码建议,但这是一个关键问题,需要在此处指出。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/picker/picker.tsx (1)

142-146: 与上述相同的依赖数组问题。

此处 [value] 依赖数组未包含 visiblesetInnerValue,存在同样的 ESLint 规则警告风险。建议一并添加注释说明意图。

🤖 Fix all issues with AI agents
In `@src/components/picker/picker.tsx`:
- Around line 133-140: The useEffect that resets innerValue when the picker
opens reads innerValue and value but its dependency array only lists [visible,
props.selectValue], which triggers react-hooks/exhaustive-deps warnings and
risks stale closures; update the effect to either include the missing
dependencies (innerValue and value and setInnerValue) or explicitly document the
intent with an eslint-disable-next-line comment, or switch to a ref pattern
(e.g., valueRef.current) and reference valueRef in the effect; adjust the effect
around useEffect / props.selectValue / visible / innerValue / value /
setInnerValue accordingly so the linter is satisfied and closures are safe.

Comment on lines 133 to +140
useEffect(() => {
if (innerValue !== value) {
setInnerValue(value)
// 仅在非受控模式下,当 picker 打开时,重置 innerValue
if (props.selectValue === undefined && visible) {
if (innerValue !== value) {
setInnerValue(value)
}
}
}, [visible])
}, [visible, props.selectValue])
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

依赖数组不完整,可能引发 ESLint 警告或闭包陷阱。

useEffect 内部读取了 innerValuevalue,但依赖数组仅包含 [visible, props.selectValue]。虽然当前逻辑意图明确(仅在 visibleselectValue 变化时触发),但这种写法:

  1. 会触发 react-hooks/exhaustive-deps 规则警告
  2. 后续代码修改时可能产生意外的闭包问题

建议添加 eslint-disable 注释明确意图,或考虑使用 ref 来获取最新值:

🔧 建议的修复方案

方案 1:添加注释说明意图

  useEffect(() => {
    // 仅在非受控模式下,当 picker 打开时,重置 innerValue
    if (props.selectValue === undefined && visible) {
      if (innerValue !== value) {
        setInnerValue(value)
      }
    }
+   // eslint-disable-next-line react-hooks/exhaustive-deps -- 仅在 visible 或 selectValue 变化时触发,intentionally 读取最新的 innerValue 和 value
  }, [visible, props.selectValue])

方案 2:使用 ref 保存最新值(如果项目有此模式)

const valueRef = useRef(value)
valueRef.current = value

useEffect(() => {
  if (props.selectValue === undefined && visible) {
    setInnerValue(valueRef.current)
  }
}, [visible, props.selectValue, setInnerValue])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (innerValue !== value) {
setInnerValue(value)
// 仅在非受控模式下,当 picker 打开时,重置 innerValue
if (props.selectValue === undefined && visible) {
if (innerValue !== value) {
setInnerValue(value)
}
}
}, [visible])
}, [visible, props.selectValue])
useEffect(() => {
// 仅在非受控模式下,当 picker 打开时,重置 innerValue
if (props.selectValue === undefined && visible) {
if (innerValue !== value) {
setInnerValue(value)
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps -- 仅在 visible 或 selectValue 变化时触发,intentionally 读取最新的 innerValue 和 value
}, [visible, props.selectValue])
🤖 Prompt for AI Agents
In `@src/components/picker/picker.tsx` around lines 133 - 140, The useEffect that
resets innerValue when the picker opens reads innerValue and value but its
dependency array only lists [visible, props.selectValue], which triggers
react-hooks/exhaustive-deps warnings and risks stale closures; update the effect
to either include the missing dependencies (innerValue and value and
setInnerValue) or explicitly document the intent with an
eslint-disable-next-line comment, or switch to a ref pattern (e.g.,
valueRef.current) and reference valueRef in the effect; adjust the effect around
useEffect / props.selectValue / visible / innerValue / value / setInnerValue
accordingly so the linter is satisfied and closures are safe.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 27, 2026

npm i https://pkg.pr.new/ant-design/ant-design-mobile/antd-mobile@7024

commit: 336cf30

@codecov
Copy link

codecov bot commented Jan 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.96%. Comparing base (006e607) to head (336cf30).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7024      +/-   ##
==========================================
+ Coverage   92.94%   92.96%   +0.01%     
==========================================
  Files         337      337              
  Lines        7375     7376       +1     
  Branches     1879     1844      -35     
==========================================
+ Hits         6855     6857       +2     
+ Misses        512      511       -1     
  Partials        8        8              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

columns: PickerColumn[] | ((value: PickerValue[]) => PickerColumn[])
value?: PickerValue[]
defaultValue?: PickerValue[]
selectValue?: PickerValue[]
Copy link
Member

Choose a reason for hiding this comment

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

感觉直接 defaultValue 就够了?

Copy link
Author

Choose a reason for hiding this comment

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

不行的哇!我试过了

Copy link
Author

@lzm0x219 lzm0x219 Jan 27, 2026

Choose a reason for hiding this comment

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

我的场景是打开 picker 组件后,需要动态指定 selected 值,目前组件无法满足的

Copy link
Member

Choose a reason for hiding this comment

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

那和 antd 一样叫 defaultPickerValue 好了

Copy link
Author

Choose a reason for hiding this comment

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

ok,我改一下字段名

Copy link
Member

Choose a reason for hiding this comment

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

哦,我get 了。的确是 Picker 和 antd 的 DatePicker 不一样。我明白你说 selectValue 的意思。不过即便这样,selectValue 也应该是受控属性,需要 onSelect 改了才能改 selectValue

Copy link
Author

Choose a reason for hiding this comment

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

是这样的

Copy link
Author

Choose a reason for hiding this comment

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

目前我的修改能满足这个需求,并且也是受控的

Copy link
Member

Choose a reason for hiding this comment

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

👍🏻 对应的测试用例也补充一下吧~

Copy link
Author

Choose a reason for hiding this comment

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

ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants