Base UI CheckboxGroup 静默失效:是 value 在匹配,不是 name
引入 @base-ui-components/react 的 CheckboxGroup,照着直觉把每个子 Checkbox 的标识符写在 name 上,再把同一批字符串塞进 defaultValue——看起来无懈可击。跑起来之后,却发现所有复选框都没有选中态,点击也没有任何回调。DOM 检查一切正常:role="group" 在,三个 role="checkbox" 都在,渲染完全没问题。
这是一种最难排查的 bug——组件「装好了」,却什么都不动。
现象
给 Checkbox 传 name="apple",给 CheckboxGroup 传 defaultValue={["apple"]}:
// 看起来合理,实际完全不生效
<CheckboxGroup defaultValue={["apple"]} onValueChange={(v) => console.log(v)}>
<Checkbox name="apple" />
<Checkbox name="banana" />
</CheckboxGroup>aria-checked恒为false,defaultValue不生效- 点击后
onValueChange收到undefined,或根本不触发 - DOM 结构渲染完全正常,
role="group"+ 子role="checkbox"全部存在 - 极易被误判为「组件已正常接入,是状态管理哪里出了问题」
根因
翻开 @base-ui-components/react/checkbox/root/CheckboxRoot.js 源码:
// line 126-127 —— 勾选态完全由 value 匹配,name 不参与
controlled: value && groupValue && !parent
? groupValue.includes(value)
: groupChecked,
default: value && defaultGroupValue && !parent
? defaultGroupValue.includes(value)
: defaultChecked,
// line 200 —— 写回也用 value
if (value && groupValue && setGroupValue && !parent) { ... }name 在源码里只出现在表单提交路径(line 178: name: parent ? undefined : name),与 group 协调完全无关。
CheckboxGroup 的整个成员匹配机制——初始选中态、受控 value、onValueChange 写回——全部走 value prop,name 是透明的。
HTML 原生 checkbox 里,name 是表单分组键,功能上和「哪个选项被选中」高度相关,这个心智模型带进 Base UI 会踩坑。
Base UI 的 CheckboxGroup 是受控组合件,用 value 数组做成员匹配。name 仅在 <form> 提交时透传到底层 <input>,根本不参与 group 状态协调。
用 name 而不是 value 的后果:defaultValue/受控 value/onValueChange 全部静默失效,没有任何警告。
正解
将每个 Checkbox 的标识符从 name 改为 value,group 的 defaultValue/value 数组同步对应:
<CheckboxGroup defaultValue={["apple"]} onValueChange={(v) => console.log(v)}>
<Checkbox value="apple" label="苹果" />
<Checkbox value="banana" label="香蕉" />
</CheckboxGroup>如果还需要表单提交,value 和 name 可以同时传,互不干扰:
<Checkbox value="apple" name="fruit" label="苹果" />value 驱动 group 的勾选态和回调;name 驱动原生表单提交字段名。两者职责独立,同时传没有冲突。
测试坑(jsdom 下交互验证失效)
分组 Checkbox 的切换走 Base UI 内部的 pointer 事件,jsdom 环境下以下三种写法均不触发 onValueChange:
fireEvent.click(span[role=checkbox])- 直接点击隐藏的
<input> - 点击外层
<label>
实测全部静默。
单测只能断言静态态:通过 defaultValue/受控 value 验证 aria-checked,验证多选共存、disabled 向下传递、role 计数。交互正确性需要 Playwright 或截图测试兜底,不要在 jsdom 里死磕点击回调。
一句话外卖
Base UI
CheckboxGroup用value匹配成员,name只管表单提交——把 HTML 原生 checkbox 的name心智模型带进来,会踩一个完全静默的坑。