
Base UI CheckboxGroup 完全指南类型 API、状态推导与受控/非受控多选实现【免费下载链接】base-uiUnstyled UI components for building accessible web apps and design systems. From the creators of Radix, Floating UI, and Material UI.项目地址: https://gitcode.com/GitHub_Trending/ba/base-ui本文以 Base UIRadix、Floating UI、Material UI 同源团队出品的 Unstyled 组件库的CheckboxGroup组件为对象系统讲解其在当前仓库中的类型定义与 API 契约包括 Props 全表、Data Attributes、State/ChangeEventDetails等派生类型、Canonical Types 映射关系以及受控/非受控值管理、父级复选框联动allValuesparent、表单与校验集成等实战用法。读完本文你将能基于 packages/react/src/checkbox-group 的源码证据准确选用正确的 Props、正确消费事件与状态类型并快速定位底层实现与测试用例。从官方文档到仓库实现CheckboxGroup 是什么Base UI 的官方组件文档位于 docs/src/app/(docs)/react/components/checkbox-group/page.mdx/react/components/checkbox-group/page.mdx)其中对CheckboxGroup的定义是为一组复选框checkboxes提供共享状态Provides shared state to a series of checkboxes。与同一仓库中的 Checkbox 组件配套使用——CheckboxGroup负责维护哪些值被选中的公共状态而每一个Checkbox.Root负责单个复选项的渲染与交互。本篇文章对应的关联文档是 types.md/react/components/checkbox-group/types.md)。它是通过docs/src/utils/createTypes.ts的createTypes(import.meta.url, CheckboxGroup)见 types.ts/react/components/checkbox-group/types.ts)从源码自动生成的类型/API 参考页文档头部也标注了Autogenerated并提示通过pnpm docs:validate重新生成。因此本文的核心论据全部指向真实的 TypeScript 类型与实现组件实现CheckboxGroup.tsx上下文共享状态如何传递给子复选框CheckboxGroupContext.ts父子联动逻辑useCheckboxGroupParent.ts数据属性常量CheckboxGroupDataAttributes.ts出口public APIindex.ts阅读下文时你可以随时打开上述文件核对每个类型与文档描述的一一对应关系。CheckboxGroup Props 全表与类型契约CheckboxGroup通过forwardRef暴露根元素为div并带有rolegroup与aria-labelledby指向标签元素的 id源码见 CheckboxGroup.tsx 第 161-174 行。其 Props 完整定义在 CheckboxGroupProps继承自BaseUIComponentPropsdiv, CheckboxGroupState。下表与文档 types.md/react/components/checkbox-group/types.md) 保持一致并补充了源码注释中的说明Prop类型默认值说明defaultValuestring[]-省略时视为空数组EMPTY_ARRAY组内初始应处于勾选状态的复选框名称。要渲染受控组请改用value。valuestring[]-组内应处于勾选状态的复选框名称。要渲染非受控组请改用defaultValue。onValueChange(value: string[], eventDetails: CheckboxGroup.ChangeEventDetails) void-组内任一复选框被勾选/取消勾选时触发新值作为第一个参数传入。allValuesstring[]-组内所有复选框的名称列表创建父级复选框parent checkbox时必填。disabledbooleanfalse是否忽略用户交互禁用整个组。classNamestring \| ((state: CheckboxGroup.State) string \| undefined)-应用到根元素的 CSS 类或根据组件状态返回类的函数。styleReact.CSSProperties \| ((state: CheckboxGroup.State) React.CSSProperties \| undefined)-应用到根元素的样式或根据状态返回样式对象的函数。renderReactElement \| ((props: HTMLProps, state: CheckboxGroup.State) ReactElement)-将根元素替换为其他标签或与其他组件组合如Fieldset.Root render{CheckboxGroup /}。关于默认值的两个细节来自源码实现CheckboxGroup.tsx 第 60-67 行defaultValue省略时内部会退化为空数组defaultValueProp ?? EMPTY_ARRAYvalue与defaultValue通过useControlled来自base-ui/utils/useControlled统一管理受控/非受控状态传入value时组件完全受控不传时以defaultValue作为初始值进入非受控模式。关于value数组语义的几个要点从useCheckboxGroupParent.ts的实现与CheckboxGroup.test.tsx的测试可以确认value数组的语义元素是复选框的value/name字符串不是索引或 React key。官方示例使用[fuji-apple, gala-apple]这类业务值。顺序即点击顺序测试断言onValueChange收到的数组依次为[red]→[red, green]→[red, green, blue]取消勾选后为[green]见 CheckboxGroup.test.tsx 的prop: onValueChange分组。支持空字符串值测试supports an empty string item value验证了value的复选项可以被正确勾选/取消。受控值变为undefined时按空数组处理测试treats a controlled value that becomes undefined as an empty array说明即使受控值被外部清空为undefined组件也不会崩溃。defaultValue{null}被当作空数组测试treats null as an empty array这是对 JavaScript 消费方传入非预期值时的宽容处理。Data Attributes无样式组件的状态钩子CheckboxGroup暴露的根元素数据属性只有一个定义于 CheckboxGroupDataAttributes.tsAttribute类型说明data-disabled-当复选框组被禁用时出现。由于 Base UI 是 Unstyled 组件data-disabled是 CSS 选择状态的首选钩子例如[data-disabled] { opacity: 0.6; pointer-events: none; }另外虽然类型文档只列出了data-disabled但组件内部经由stateAttributesMapping: fieldValidityMappingCheckboxGroup.tsx 第 173 行还会根据 Field 状态输出表单相关的状态属性。这一点在CheckboxGroup.test.tsx的Field分组中有明确验证[data-dirty]组值与初始值不一致时出现再次还原后消失[data-filled]只要组值非空即出现即使没有渲染出对应的复选框测试[data-filled] follows the group value even without a matching rendered checkbox校验错误时子复选框会出现aria-invalid。派生类型State、ChangeEventDetails 与 ChangeEventReasonCheckboxGroup.State文档 types.md/react/components/checkbox-group/types.md) 给出了完整定义其 TypeScript 源码在 CheckboxGroupState继承自FieldRootStatetype CheckboxGroupState { /** Whether the component should ignore user interaction. */ disabled: boolean; /** Whether the field has been touched. */ touched: boolean; /** Whether the field value has changed from its initial value. */ dirty: boolean; /** Whether the field is valid. */ valid: boolean | null; /** Whether the field has a value. */ filled: boolean; /** Whether the field is focused. */ focused: boolean; };disabled由fieldDisabled || disabledProp合并而来CheckboxGroup.tsx 第 59 行其余字段来自Field上下文。该 State 被用于三类场景className/style的函数式形式例如className{(state) state.filled ? is-filled : }render回调的第二个参数state与CheckboxGroup.State命名空间类型对应见下文 Canonical Types。CheckboxGroup.ChangeEventReasontype CheckboxGroupChangeEventReason none;这是 Base UI 统一的BaseUIEventReasons[none]来自internals/reasons表示目前CheckboxGroup的变更事件没有细分触发原因统一为none。子组件如父级复选框的onCheckedChange同样复用该 reason。CheckboxGroup.ChangeEventDetailstype CheckboxGroupChangeEventDetails { /** The reason for the event. */ reason: none; /** The native event associated with the custom event. */ event: Event; /** Cancels Base UI from handling the event. */ cancel: () void; /** Allows the event to propagate in cases where Base UI will stop the propagation. */ allowPropagation: () void; /** Indicates whether the event has been canceled. */ isCanceled: boolean; /** Indicates whether the event is allowed to propagate. */ isPropagationAllowed: boolean; /** The element that triggered the event, if applicable. */ trigger: Element | undefined; };ChangeEventDetails是 Base UI 的BaseUIChangeEventDetails泛型实例化CheckboxGroup.tsx 第 219-220 行。其中cancel()是重点在onValueChange中调用eventDetails.cancel()可以阻止组状态更新。源码中setValue包装器CheckboxGroup.tsx 第 69-79 行先调用onValueChange再检查isCanceled决定是否写入新值测试does not update the group when onValueChange cancels the event验证了即使onValueChange收到[red]由于调用了cancel()所有复选框的aria-checked仍然保持false。这为先确认再改状态的业务场景如配额校验、二次确认提供了官方机制。Canonical Types命名空间别名与使用建议文档 types.md/react/components/checkbox-group/types.md) 的 Canonical Types 一节给出了类型别名映射表其来源是源码末尾的namespace CheckboxGroup声明CheckboxGroup.tsx 第 222-227 行CanonicalAlias使用建议CheckboxGroup.StateCheckboxGroupState已导入CheckboxGroup命名空间时用左侧否则用右侧CheckboxGroup.PropsCheckboxGroupProps同上CheckboxGroup.ChangeEventReasonCheckboxGroupChangeEventReason同上CheckboxGroup.ChangeEventDetailsCheckboxGroupChangeEventDetails同上// 方式一使用命名空间类型已 import CheckboxGroup function handleChange(value: string[], details: CheckboxGroup.ChangeEventDetails) { // ... } // 方式二使用独立别名 import type { CheckboxGroupChangeEventDetails } from base-ui/react/checkbox-group;CheckboxGroup.Props是对CheckboxGroupProps的 Re-export两者完全等价。这是 Base UI 的整体风格组件在运行时是值value同时作为命名空间承载配套类型。上下文与共享状态机制CheckboxGroup之所以能让一组复选框共享状态是因为它通过CheckboxGroupContext.Provider向下传递上下文CheckboxGroup.tsx 第 176-178 行。上下文结构定义在 CheckboxGroupContext.tsinterface CheckboxGroupContext { value: string[]; setValue: (value: string[], eventDetails: BaseUIChangeEventDetails...) void; allValues: string[] | undefined; parent: UseCheckboxGroupParentReturnValue; disabled: boolean; validation: UseFieldValidationReturnValue; registerControlId: LabelableContext[registerControlId]; }其中setValue是经useStableCallback包装的稳定回调内部先触发onValueChange、再按isCanceled决定是否提交见上文事件详情部分parent是useCheckboxGroupParent的返回值包含getParentProps/getChildProps/registerChildId/disabledStatesRef专门服务于父级复选框场景validation来自Field根组件使CheckboxGroup可以直接参与字段校验见下文表单与校验registerControlId用于标签作用域注释明确指出复选框如果看到同一个registerControlId就与组共享标签作用域组本身才是字段的控件而不是组内某个任意复选框。父级复选框allValues parent 的联动原理官方三步用法文档 page.mdx/react/components/checkbox-group/page.mdx) 的 Parent checkbox 一节给出了创建全选/取消全选父级复选框的步骤让CheckboxGroup成为受控组件传入value与onValueChange把组内所有子复选框的值数组传给allValues在父级Checkbox.Root上加parent布尔 prop。当部分而非全部子复选框被勾选时组会控制父级复选框的indeterminate状态。完整可运行示例见 demos/parent/css-modules/index.tsx/react/components/checkbox-group/demos/parent/css-modules/index.tsx)三选一的全选示例用state.indeterminate渲染横线图标const fruits [fuji-apple, gala-apple, granny-smith-apple]; CheckboxGroup value{value} onValueChange{setValue} allValues{fruits} aria-labelledby{id} label id{id} Checkbox.Root parent Checkbox.Indicator render{(props, state) ( span {...props}{state.indeterminate ? HorizontalRuleIcon / : CheckIcon /}/span )} / /Checkbox.Root Apples /label {/* 子项valuefuji-apple / gala-apple / granny-smith-apple */} /CheckboxGroup底层实现useCheckboxGroupParent父子联动的核心逻辑在 useCheckboxGroupParent.ts状态机status取on | off | mixed三态。checked value.length allValues.lengthindeterminate value.length ! allValues.length value.length 0。当父级在mixed态被点击时下一状态为on全选on时点击则变为off全不选——测试preserves initial state if mixed when parent is clicked与does not advance the parent toggle cycle when the group cancels a parent change详细验证了这一循环后者还确认如果组的onValueChange调用了cancel()内部状态不会推进到下一个状态再次点击会重试同一个mixed → on转换。禁用项处理getParentProps中的onCheckedChange会通过disabledStatesRef过滤禁用项。未勾选的禁用项不会被全选已勾选的禁用项在全不选时保持勾选。测试handles unchecked disabled checkboxes/handles checked disabled checkboxes分别覆盖了这两种情况。可访问性父级复选框通过registerChildId收集每个子复选项真实渲染出的id组装成空格分隔的aria-controls测试should apply space-separated aria-controls attribute with child names。子项卸载时对应 id 会被清理drops an unmounted child from aria-controls。注册表使用Map而非普通对象规避了constructor这类值名带来的原型链读取风险测试does not read aria-controls ids off Object.prototype。取消传播父级或子级复选框都可以通过自己的onCheckedChange调用eventDetails.cancel()来阻止组变更测试lets a parent checkbox cancel a parent-enabled group change/lets a child checkbox cancel...。嵌套父级复选框嵌套场景如权限组包含子权限组的完整示例见 demos/nested/css-modules/index.tsx/react/components/checkbox-group/demos/nested/css-modules/index.tsx)外层CheckboxGroup以mainPermissions为allValues内层以userManagementPermissions为allValues勾选外层Manage Users时联动填充内层全部子项取消时清空内层内层任一变化又会反向更新外层manage-users的勾选状态。这是一个典型的级联勾选模式CheckboxGroup value{mainValue} onValueChange{...} allValues{mainPermissions} {/* 外层父项User Permissionsparent */} CheckboxGroup value{managementValue} onValueChange{...} allValues{userManagementPermissions} {/* 内层父项Manage Usersparent以及 create-user / edit-user / ... */} /CheckboxGroup /CheckboxGroup注意内层父项还可以显式传入indeterminate来增强半选态表现源码第 35-38 行。表单集成与校验与 Field / Fieldset / Form 协同文档 page.mdx/react/components/checkbox-group/page.mdx) 的 Form integration 一节展示了把CheckboxGroup渲染为Fieldset的推荐写法——通过render将CheckboxGroup与Fieldset.Root组合每个复选项包在Field.ItemField.Label中Form Field.Root nameallowedNetworkProtocols Fieldset.Root render{CheckboxGroup /} Fieldset.LegendAllowed network protocols/Fieldset.Legend Field.Item Field.Label Checkbox.Root valuehttp / HTTP /Field.Label /Field.Item {/* https / ssh 同理 */} /Fieldset.Root /Field.Root /Form从源码看CheckboxGroup与表单深度集成CheckboxGroup.tsx 第 103-141 行getFormValue遍历validation.registeredInputs只把已勾选且可提交的输入值过滤出来作为提交给表单的值useRegisterFieldControl将组注册为字段控件fieldName存在且未禁用时才注册值变化时通过useValueChanged触发清除对应字段错误clearErrors、比对初始值计算dirty、调用validation.change触发校验。CheckboxGroup.test.tsx的Field分组对校验行为有非常细的测试覆盖包括required 语义组内多个复选框都是required时只勾选其中一个不能消除valueMissing错误必须全部勾选keeps a required error while another required checkbox in the group is unchecked禁用项豁免禁用且required的复选项不参与约束校验ignores a disabled required checkbox when validating the group卸载后的校验已勾选的复选项卸载后组仍会基于注册表继续校验剩余必需项keeps validating the remaining required checkbox after a checked sibling unmounts三种校验模式validationModeonChange/onBlur/onSubmit均有对应测试验证自定义validate函数收到的值始终是组的完整string[]外部受控变更也触发重校验revalidates when the controlled value changes externally。可访问性要点命名与标签文档 page.mdx/react/components/checkbox-group/page.mdx) 的 Usage guidelines 强调表单控件必须有可访问名称accessible name。CheckboxGroup的三种命名方式aria-labelledby 兄弟标签div idprotocols-labelAllowed network protocols/div配合CheckboxGroup aria-labelledbyprotocols-label包裹式label每个复选项用labelCheckbox.Root valuehttp / HTTP/label包裹——默认情况下Checkbox.Root渲染为span因此支持包裹标签Field / Fieldset 组件见上文表单集成一节。文档还特别说明了一个细节Rendering as a native button默认Checkbox.Root渲染span以支持包裹式 label当改用兄弟标签htmlFor/id时应把每个复选框渲染为原生buttonnativeButtonrender{button /}因为原生按钮 兄弟标签是无障碍语义更好的组合。若想用原生按钮又保留包裹式label则用render回调把button放进label中避免无效 HTML隐藏 input 会放到 label 外部。源码层面CheckboxGroup根元素自带rolegroup与aria-labelledby而useLabelableId({ id: null })CheckboxGroup.tsx 第 89 行确保Field.Label的htmlFor不会错误地指向组内某个任意复选框——组本身才是字段控件。Field.Label分组测试验证了这一点hydration 后 label 的for属性被移除aria-labelledby指向 label 的 idlabels the group rather than pointing Field.Label at one checkbox inside it并保证共享 Field.Root 时各复选项 id 唯一keeps checkbox ids unique when the group shares one Field.Root。结语从类型契约到工程实践的完整闭环CheckboxGroup是 Base UI 中小而专的组件类型层面它以CheckboxGroup.Props / .State / .ChangeEventDetails命名空间清晰地定义了 API 契约实现层面它用useControlled统一受控/非受控、用CheckboxGroupContext分发共享状态、用useCheckboxGroupParent支撑全选/半选/级联联动、用 Field 体系打通校验与表单提交测试层面CheckboxGroup.test.tsx 与 useCheckboxGroupParent.test.tsx 覆盖了取消传播、禁用项、卸载后校验、SSR 唯一 id 等边界场景。当你需要在自己的应用中实现多选 全选/半选 表单校验的复选项组时可以直接参考 demos/parent/css-modules/index.tsx/react/components/checkbox-group/demos/parent/css-modules/index.tsx)全选联动与 demos/nested/css-modules/index.tsx/react/components/checkbox-group/demos/nested/css-modules/index.tsx)嵌套权限级联再对照本文的 API 表选择正确的 Props 与类型即可避免大多数类型与状态同步上的常见坑。【免费下载链接】base-uiUnstyled UI components for building accessible web apps and design systems. From the creators of Radix, Floating UI, and Material UI.项目地址: https://gitcode.com/GitHub_Trending/ba/base-ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考