Ant Design Radio.Group 互斥单选框组:基本用法、配置参数与源码级原理解析
2026/9/19 21:35:52 网站建设 项目流程

Ant Design Radio.Group 互斥单选框组:基本用法、配置参数与源码级原理解析

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design

导读

在 Ant Design(antd)中,单个Radio只是一个可勾选的圆圈,真正让多个单选形成「一组互斥选项」的,是Radio.Group容器。本指南以 components/radio/demo/radiogroup.md 中「一组互斥的 Radio 配合使用」这一核心语义为骨架,完整讲解Radio.Group的受控用法、options声明式配置、按钮形态、禁用与尺寸等全部配置项,并结合仓库源码剖析其「受控/非受控状态管理」与「Context 广播选中值」的底层实现。读完本文,你将能够熟练使用Radio.Group构建任何互斥选择场景,并理解其内部工作机制。

一、互斥的本质:为什么单选必须放进 Radio.Group

radiogroup.md的说明非常凝练——zh-CN 为「一组互斥的 Radio 配合使用」,en-US 为 "A group of radio components"。这句话点出了 Radio 组件体系中最核心的设计:单选互斥不是每个Radio自己实现的,而是由外层Radio.Group统一协调的

从 radio.tsx 的源码可以看到,单个Radio内部通过React.useContext(RadioGroupContext)读取组上下文,并在点击时依次触发自身props.onChange与组上下文中的groupContext.onChange

const onChange = (e: RadioChangeEvent) => { props.onChange?.(e); groupContext?.onChange?.(e); };

也就是说,选中状态是「组」级别的共享状态:组内任意一个Radio被点击,事件会上报到Radio.Group,由组更新当前值,再通过 Context 广播给组内所有Radio,让它们重新计算各自的checked(见 radio.tsx):

if (groupContext) { radioProps.name = groupContext.name; radioProps.onChange = onChange; radioProps.checked = props.value === groupContext.value; radioProps.disabled = radioProps.disabled ?? groupContext.disabled; }

checked = props.value === groupContext.value这一行就是「互斥」的底层逻辑:同一时刻组内只有一个Radiovalue与组当前值相等,因此永远只选中一项。

二、基本用法:受控模式下的一组互斥 Radio

radiogroup.md对应的演示代码 components/radio/demo/radiogroup.tsx 给出了最经典、最完整的受控用法,这也是本文所有例子的基础。下面完整展开并逐点注释:

import React, { useState } from 'react'; import type { RadioChangeEvent } from 'antd'; import { Radio } from 'antd'; const App: React.FC = () => { // 用 useState 维护当前选中的值,默认选中 value 为 1 的 Radio const [value, setValue] = useState(1); // 组内任意 Radio 被点击都会触发 onChange,e.target.value 即被点击项的 value const onChange = (e: RadioChangeEvent) => { console.log('radio checked', e.target.value); setValue(e.target.value); }; return ( <Radio.Group onChange={onChange} value={value}> <Radio value={1}>A</Radio> <Radio value={2}>B</Radio> <Radio value={3}>C</Radio> <Radio value={4}>D</Radio> </Radio.Group> ); }; export default App;

要点说明:

  • value+onChange构成受控模式value决定当前选中项,onChange在用户点击时被回调。用户点击 A/B/C/D 中任意一项,e.target.value就是该项的value,将其setValue回去即完成受控更新。
  • value可以是任意类型(数字、字符串等),只要与各Radiovalue一一对应即可;本例中value是数字1/2/3/4
  • 互斥效果开箱即用:不需要给每个Radio手动设置checked,组会统一处理。
  • 如果你希望初始有一个默认选中项,且后续不关心受控更新,可以改用defaultValue(详见下文 API 表)。

相关演示的完整清单

仓库 components/radio/demo 目录围绕「Radio 组」场景提供了成体系的演示,均可作为实战参考:

演示文件主题
radiogroup.tsx一组互斥 Radio 的基本用法(本文主体)
radiogroup-options.tsx使用options数组声明式渲染选项
radiogroup-more.tsx组内嵌入「更多…」输入框等自定义内容
radiogroup-with-name.tsx为组内所有 Radio 统一设置原生name
radiobutton.tsx按钮风格的单选组
radiobutton-solid.tsx实心(solid)按钮风格
disabled.tsx禁用状态
size.tsx三种尺寸切换

三、options属性:用数组声明式渲染一组选项

除了把Radio作为children手写,Radio.Group还支持通过options属性声明式渲染,代码更简洁,也便于从后端数据直接驱动。演示代码 components/radio/demo/radiogroup-options.tsx 展示了全部三种写法:

import React, { useState } from 'react'; import type { RadioChangeEvent } from 'antd'; import { Radio } from 'antd'; // 写法一:纯字符串数组 const plainOptions = ['Apple', 'Pear', 'Orange']; // 写法二:对象数组(label + value,可附加 title 等) const options = [ { label: 'Apple', value: 'Apple' }, { label: 'Pear', value: 'Pear' }, { label: 'Orange', value: 'Orange', title: 'Orange' }, ]; // 写法三:对象数组 + 单项 disabled const optionsWithDisabled = [ { label: 'Apple', value: 'Apple' }, { label: 'Pear', value: 'Pear' }, { label: 'Orange', value: 'Orange', disabled: true }, ]; const App: React.FC = () => { const [value1, setValue1] = useState('Apple'); const [value2, setValue2] = useState('Apple'); const [value3, setValue3] = useState('Apple'); const [value4, setValue4] = useState('Apple'); const onChange1 = ({ target: { value } }: RadioChangeEvent) => { console.log('radio1 checked', value); setValue1(value); }; // onChange2 / onChange3 / onChange4 结构相同,此处省略 return ( <> {/* 字符串数组 */} <Radio.Group options={plainOptions} onChange={onChange1} value={value1} /> <br /> {/* 对象数组,其中一项 disabled */} <Radio.Group options={optionsWithDisabled} onChange={onChange2} value={value2} /> <br /> <br /> {/* 按钮形态(outline) */} <Radio.Group options={options} onChange={onChange3} value={value3} optionType="button" /> <br /> <br /> {/* 按钮形态 + 实心样式 */} <Radio.Group options={optionsWithDisabled} onChange={onChange4} value={value4} optionType="button" buttonStyle="solid" /> </> ); }; export default App;

options支持的数据形态

结合 group.tsx 的源码实现,options的解析逻辑非常清晰:

  1. string | number基本类型数组:每一项直接作为该Radiovalue和显示文本,例如['Apple', 'Pear', 'Orange']
  2. 对象数组:支持以下字段:
字段类型说明
labelReactNode选项显示文本
valueany选项值,用于互斥比较与onChange回调
disabledboolean仅禁用该选项(优先级与组级disabled合并,见下)
titlestring选项的原生title提示
styleCSSProperties作用于该选项的样式
idstring该选项的原生id
requiredboolean该选项的原生required标记

源码中对象形态的渲染逻辑(group.tsx)为:

return ( <Radio key={`radio-group-value-options-${option.value}`} prefixCls={prefixCls} disabled={option.disabled || disabled} value={option.value} checked={value === option.value} title={option.title} style={option.style} id={option.id} required={option.required} > {option.label} </Radio> );

注意disabled={option.disabled || disabled}这行:单项disabled与组级disabled是「或」的关系,组级禁用后即使单项未声明也会被禁用;反之,组未禁用时,可以通过单项disabled: true单独禁用一个选项(如上面的optionsWithDisabled中 Orange 不可选)。

四、进阶场景:按钮形态、禁用、尺寸与自定义内容

1. 按钮形态optionType="button"与实心buttonStyle="solid"

单选组有两种展示形态:默认的「圆点 + 文本」和「按钮」形态。通过optionType="button"切换为按钮组,再配合buttonStyle选择描边(outline,默认)或实心(solid)样式:

<Radio.Group defaultValue="a" optionType="button" buttonStyle="solid"> <Radio.Button value="a">Hangzhou</Radio.Button> <Radio.Button value="b">Shanghai</Radio.Button> <Radio.Button value="c">Beijing</Radio.Button> </Radio.Group>
  • Radio.Group上设置optionType="button"时,组内Radio会渲染为按钮样式;等价地,也可以使用复合组件Radio.Button显式声明,其实现(radioButton.tsx)正是通过RadioOptionTypeContextProvideroptionType注入为'button'
  • 样式切换的底层逻辑在 radio.tsx:optionType === 'button'时,prefixClsant-radio变为ant-radio-button,从而命中按钮形态的样式与波纹效果。
  • 注意:optionType仅在Radio.Group上受支持,直接在单个Radio上使用optionType会在开发环境触发 antd 的 usage 警告(见 radio.tsx)。

2. 禁用:组级禁用与单项禁用

// 整组禁用 <Radio.Group defaultValue="a" disabled> <Radio value="a">A</Radio> <Radio value="b">B</Radio> </Radio.Group> // 仅禁用一个选项(options 写法) <Radio.Group defaultValue="a" options={[{ label: 'A', value: 'a' }, { label: 'B', value: 'b', disabled: true }]} />

从源码看,disabled有三层来源并依次合并:单项props.disabled→ 组上下文groupContext.disabled(由Radio.Groupdisabled提供)→ 全局DisabledContext(由ConfigProvider disabledForm表单项注入),见 radio.tsx。

3. 尺寸size

Radio.Groupsize接受large/middle/small,不传时通过useSize自动继承ConfigProvider的全局size配置(group.tsx),并生成ant-radio-group-large/middle/small修饰类(group.tsx)。

<Radio.Group defaultValue="a" size="large"> <Radio.Button value="a">Hangzhou</Radio.Button> <Radio.Button value="b">Shanghai</Radio.Button> </Radio.Group>

4. 组内嵌入自定义内容:动态「更多…」输入框

演示 radiogroup-more.tsx 展示了一个实用的组合技巧——当选中「More...」时动态渲染一个输入框:

const [value, setValue] = useState(1); <Radio.Group onChange={onChange} value={value}> <Space direction="vertical"> <Radio value={1}>Option A</Radio> <Radio value={2}>Option B</Radio> <Radio value={3}>Option C</Radio> <Radio value={4}> More... {value === 4 ? <Input style={{ width: 100, marginInlineStart: 10 }} /> : null} </Radio> </Space> </Radio.Group>

由于Radiochildren只是普通内容插槽(见 radio.tsx 的<span>{children}</span>),在组内混入InputSpace等任意内容是完全合法的,这为「其他」选项 + 补充输入框这类常见交互提供了优雅的解法。

五、Radio.GroupAPI 速查(源自 interface.ts)

以下属性均定义在 components/radio/interface.ts 的RadioGroupProps中,是官方支持的完整配置面:

属性类型默认值说明
valueany-受控值,指定当前选中的Radiovalue
defaultValueany-非受控模式下的初始选中值
onChange(e: RadioChangeEvent) => void-选项变化时的回调,e.target.value为新选中值
disabledbooleanfalse是否禁用整组
size'large' \| 'middle' \| 'small'继承ConfigProvider组尺寸,仅对按钮形态外观有明显影响
namestring-为组内所有Radio统一设置原生name,便于表单提交与原生互斥兜底
options(string \| number \| { label; value; disabled?; title?; style?; id?; required? })[]-以数组声明式渲染选项
optionType'default' \| 'button''default'展示形态:圆点单选或按钮单选
buttonStyle'outline' \| 'solid''outline'按钮形态下的样式(描边 / 实心)
idstring-容器的原生id
onMouseEnter/onMouseLeave/onFocus/onBlur事件回调-容器级事件透传(group.tsx)

此外,Radio.Group容器会自动透传aria-*data-*属性(源码中通过pickAttrs(props, { aria: true, data: true })实现,见 group.tsx),并原生支持 RTL 布局(direction === 'rtl'时添加ant-radio-group-rtl类)。

受控与非受控:useMergedState的统一

从 group.tsx 可见,Radio.Group内部通过useMergedState(props.defaultValue, { value: props.value })管理选中值:传了value即为受控模式,组值完全由外部驱动;只传defaultValue则为非受控模式,组内部自维护状态。

事件处理onRadioChange(group.tsx)的关键逻辑是:

const onRadioChange = (ev: RadioChangeEvent) => { const lastValue = value; const val = ev.target.value; if (!('value' in props)) { setValue(val); // 非受控时,内部同步状态 } const { onChange } = props; if (onChange && val !== lastValue) { onChange(ev); // 仅在选中值真正变化时回调 } };

值得注意的细节:只有值发生变化(val !== lastValue)才会触发onChange,重复点击当前选中项不会产生冗余回调,这与原生 radio 的行为保持一致。

六、源码级原理:状态如何从「组」广播到「项」

理解Radio.Group的互斥机制,核心是掌握它基于 React Context 的「单向数据流」链路。仓库中的相关实现文件为:

  • components/radio/group.tsx:组容器,负责状态管理与广播
  • components/radio/context.ts:Context 定义(RadioGroupContextRadioOptionTypeContext
  • components/radio/radio.tsx:单项,消费组上下文
  • components/radio/interface.ts:类型定义
  • components/radio/index.tsx:复合组件组装(Radio.GroupRadio.Button

一次完整交互的调用链可以归纳为:

  1. 点击:用户点击组内某个Radio,底层rc-checkbox触发onChange
  2. 上报:radio.tsx 先触发单项自身的onChange,再调用groupContext.onChange(e),把事件交给Radio.Group
  3. 更新:group.tsx 的onRadioChange取出e.target.value,在非受控模式下setValue更新组状态(受控模式下状态由外部value驱动),并在值变化时回调props.onChange
  4. 广播Radio.Group通过RadioGroupContextProvider把最新的valuedisablednameoptionTypeonChange注入 Context(group.tsx);
  5. 重算:组内每个Radio重新渲染,按props.value === groupContext.value重新计算自己的checked,于是旧选中项取消、新选中项点亮,互斥效果达成。

同时,context.ts 中还定义了独立的RadioOptionTypeContext,专门用于把「按钮形态」从Radio.Group传给内部RadioRadio.Button正是通过它注入optionType='button'),实现形态与选中状态的解耦。

七、事件对象RadioChangeEvent

onChange的回调参数RadioChangeEvent(定义于 interface.ts)结构如下:

interface RadioChangeEvent { target: RadioChangeEventTarget; // 包含 value、checked、name 等 Radio 属性 stopPropagation: () => void; preventDefault: () => void; nativeEvent: MouseEvent; }

其中targetRadioChangeEventTarget(interface.ts),除了RadioProps的全部属性外,还额外带有checked: boolean。实际开发中最常用的是e.target.value(新选中的值),也可以像演示代码那样用解构简写:

const onChange = ({ target: { value } }: RadioChangeEvent) => { setValue(value); };

八、实践要点与注意事项

  1. 互斥无需手动checked:不要在组内手动给Radiochecked,组上下文会自动计算;手动设置会与组逻辑冲突。
  2. 区分valuedefaultValue:需要外部控制(如受表单状态或请求结果驱动)时用value + onChange;仅需初始选中时用defaultValue,二者不要混用。
  3. optionType只写在Radio.Group:单独写在Radio上会触发开发环境警告;按钮形态也可以直接用Radio.Button复合组件。
  4. disabled的叠加规则:单项disabled、组级disabledConfigProvider/Form的全局DisabledContext三者取「或」,优先级从内到外依次合并。
  5. optionschildren二选一:源码中options存在且非空时优先渲染optionschildren会被忽略(group.tsx)。
  6. 原生name的兜底价值:通过Radio.Groupname属性统一设置后,组内所有Radio共享同一原生name,在无 JavaScript 的极端场景下表单提交也能保持单选语义;配合Form使用时,antd 的FormItemInputContext会自动为Radio添加ant-radio-wrapper-in-form-item修饰类以适配表单项样式。
  7. 重复点击不触发onChange:选中值未变化时回调不会被触发,依赖「每次点击都回调」的逻辑需要自行处理。

以上内容均可在当前仓库中逐一验证:演示代码见 components/radio/demo 目录,实现源码见 group.tsx、radio.tsx、radioButton.tsx、context.ts 与 interface.ts。结合这些文件,你可以在自己的项目中放心地把Radio.Group用于任何需要互斥单选的场景。

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询