Ant Design Radio 垂直 RadioGroup 实战:结合动态输入框实现多选项扩展选择
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/antde/ant-design
导读
在表单设计中,当备选项较多且需要展示完整选项供用户比较时,单选框(Radio)比下拉选择更直观。ant-design 的RadioGroup默认是水平排布,而通过简单的样式控制即可实现垂直布局,并可在某个选项被选中后动态渲染额外输入框,用于"其他 / More..."之类的扩展场景。本文将基于 ant-design 仓库中 radiogroup-more 演示 完整讲解垂直 RadioGroup 的实现原理、受控/非受控切换机制,以及如何结合 Input 组件实现条件渲染的扩展选项。
一、场景定位:什么时候需要垂直 RadioGroup
根据 Radio 组件文档 的说明,Radio 适用于"在多个备选项中选中单个状态",它与 Select 的区别在于:Radio 的所有选项默认可见,方便用户在比较中做选择,因此选项不宜过多。
当选项数量较多、文案较长,或表单采用纵向排版时,水平排布的选项会占用过多横向空间,此时将 RadioGroup 改为垂直排布是最直接的解决方案。典型的应用场景包括:
- 设置面板中"支付方式""通知频率"等纵向表单项;
- 问卷调查中逐行排列的选择题;
- "其他选项 + 自定义输入"的组合式表单(本文演示的核心场景)。
二、垂直 RadioGroup 的完整实现
radiogroup-more.md演示了"垂直的 RadioGroup,配合更多输入框选项"的用法。核心代码如下:
import { Radio, Input } from 'antd'; const RadioGroup = Radio.Group; const App = React.createClass({ getInitialState() { return { value: 1, }; }, onChange(e) { console.log('radio checked', e.target.value); this.setState({ value: e.target.value, }); }, render() { const radioStyle = { display: 'block', height: '30px', lineHeight: '30px', }; return ( <RadioGroup onChange={this.onChange} value={this.state.value}> <Radio style={radioStyle} key="a" value={1}>Option A</Radio> <Radio style={radioStyle} key="b" value={2}>Option B</Radio> <Radio style={radioStyle} key="c" value={3}>Option C</Radio> <Radio style={radioStyle} key="d" value={4}> More... {this.state.value === 4 ? <Input style={{ width: 100, marginLeft: 10 }} /> : null} </Radio> </RadioGroup> ); } }); ReactDOM.render(<App />, mountNode);2.1 垂直排布的实现要点
垂直布局并不需要额外的组件 API,而是通过radioStyle让每个 Radio 的label元素按块级元素排布:
display: 'block':将选项从内联元素改为块级元素,使每个选项独占一行;height: '30px'与lineHeight: '30px':固定行高并垂直居中,保证多选项之间视觉对齐、间距均匀。
从样式源码看,radio.less 中.ant-radio-group默认display: inline-block,其内部的label默认带margin-right: 16px(水平间距),而单个.ant-radio是display: inline-block。因此若不额外设置样式,RadioGroup 内的选项天然是水平排列的。通过内联样式覆盖display为block,即可在不修改任何全局样式的前提下实现垂直布局——这也是该演示刻意使用style而非新增 CSS 类的原因。
2.2 "More..." + 动态输入框的条件渲染
最后一个选项Option D的文案是More...,其关键在于:
{this.state.value === 4 ? <Input style={{ width: 100, marginLeft: 10 }} /> : null}当且仅当当前选中值为4(即选中了 "More..." 选项)时,才在选项文本后方渲染一个宽度 100px、左侧留白 10px 的Input。marginLeft: 10用于与"More..."文本之间保持间距,width: 100控制输入框尺寸,避免撑开布局。
这里体现了受控组件的数据流闭环:
RadioGroup的value绑定this.state.value(初始为1);- 用户点击某选项时,
onChange回调拿到e.target.value并setState; - 组件重渲染时,
value === 4判断生效,输入框随之出现或消失。
配合console.log('radio checked', e.target.value)可以在控制台实时观察选中值的变化,便于调试。
三、从源码理解 RadioGroup 的选中机制
要深入理解该演示为什么能工作,需要看 RadioGroup 的底层实现 group.jsx:
3.1 受控与默认值的优先级
getInitialState中按以下优先级初始化选中值:
if ('value' in props) { value = props.value; // 1. 受控:显式传入 value } else if ('defaultValue' in props) { value = props.defaultValue; // 2. 半受控:默认值 } else { const checkedValue = getCheckedValue(props.children); value = checkedValue && checkedValue.value; // 3. 纯非受控:从子节点 checked 推导 }本文演示使用value+onChange+setState,属于标准的受控模式。此外还提供defaultValue非受控模式(见 size 演示 中的<RadioGroup defaultValue="a">),以及从子Radio的checked属性推导初值的模式。
3.2 子选项的注入与互斥
render方法中通过React.Children.map遍历子元素,对每个Radio/RadioButton注入三个关键 props:
return React.cloneElement(radio, { ...keyProps, ...radio.props, onChange: this.onRadioChange, // 统一接管点击事件 checked: this.state.value === radio.props.value, // 依据 value 相等性判定选中 disabled: radio.props.disabled || this.props.disabled, // 组级禁用向下透传 });这正是"一组互斥的 Radio"的核心实现:每个选项的选中态完全由this.state.value与自身value的相等性决定,同一时刻只有一个选项的checked为true。因此演示中所有选项都必须提供互不相同的value(1、2、3、4),这与 RadioGroup 组合演示 中的写法一致。
3.3 变化回调的传递
onRadioChange在非受控场景下会先setState更新内部 value,然后调用this.props.onChange(ev)向上抛出原生事件。事件对象e.target.value即被选中 Radio 的value,演示中的onChange(e)正是读取该字段。
3.4 复合组件结构
从 index.jsx 可以看到Radio.Group与Radio.Button是通过挂载静态属性组合而成:
AntRadio.Button = Button; AntRadio.Group = Group;因此演示中的const RadioGroup = Radio.Group;等价于Radio.Group。单个Radio组件本身封装自rc-radio,负责渲染label包裹的原生 input 与圆点样式(见 radio.jsx),这也解释了为什么给Radio传入的style会作用到label上——正是它承载了display: block的垂直排布样式。
四、API 速查与扩展场景
结合 Radio 文档 的 API 表格,本演示涉及的参数整理如下:
Radio 参数
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| checked | 指定当前是否选中 | Boolean | false |
| defaultChecked | 初始是否选中 | Boolean | false |
| value | 根据 value 进行比较,判断是否选中 | String | 无 |
RadioGroup 参数
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| onChange | 选项变化时的回调函数 | Function(e:Event) | 无 | 无 |
| value | 用于设置当前选中的值(受控) | String | 无 | 无 |
| defaultValue | 默认选中的值(非受控) | String | 无 | 无 |
| size | 大小,只对按钮样式生效 | String | largedefaultsmall | default |
扩展场景一:组级禁用
若整组选项都需要禁用,可直接在RadioGroup上设置disabled,源码中disabled: radio.props.disabled || this.props.disabled会将其透传到每个选项(见 radiobutton 演示 第三组示例)。
扩展场景二:按钮样式组合
如果希望垂直排布的是按钮样式(Radio.Button),同样可以用display: block的思路覆盖样式;按钮样式的尺寸、圆角与选中态样式均由 radio.less 中的ant-radio-button规则提供,size参数只对按钮样式生效。
扩展场景三:与表单校验集成
"More..." 输入框出现在表单中时,可配合 ant-design 的 Form 组件将输入值纳入校验;本演示的value === 4条件判断同样适用于校验规则的动态启停(例如仅在选中 "More..." 时要求该输入框必填)。
五、小结
垂直 RadioGroup 本身并不需要特殊 API,核心技巧在于:
- 用
display: block+ 固定行高覆盖默认的水平内联布局; - 通过受控的
value+onChange维护选中状态,利用源码中checked: this.state.value === radio.props.value的相等性比较实现互斥; - 用
this.state.value === X的条件渲染在选中特定选项时动态插入Input,实现"选项 + 扩展输入"的组合交互。
文中涉及的源码与演示均可在仓库对应路径中查看:组件实现、样式定义、组件文档 以及完整的 radiogroup-more 演示。
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/antde/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考