news 2026/9/25 3:30:51

react-toolbox 单选按钮完全指南:RadioGroup / RadioButton 的用法、API 与主题定制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
react-toolbox 单选按钮完全指南:RadioGroup / RadioButton 的用法、API 与主题定制
  • 前端
  • UI组件

【免费下载链接】react-toolbox

A set of React components implementing Google's Material Design specification with the power of CSS Modules

项目地址:https://gitcode.com/gh_mirrors/re/react-toolbox
点击查看免费下载

RadioButton(单选按钮)是 Material Design 中"互斥选择"的核心控件。本文将围绕 react-toolbox 仓库中 components/radio/readme.md 展开,完整讲解 RadioGroup 与 RadioButton 的属性 API、受控用法、事件机制、源码级实现原理以及 CSS Modules 主题定制方案。读完本文,你将能在 react-toolbox 项目中正确、熟练地使用单选组件,并深入理解其底层组合工厂与主题系统。

一、什么是 RadioButton,何时该使用它

RadioButton 允许用户从一组选项中选择一个。Material Design 规范建议:当用户需要并排看到所有可选选项、且选择互斥(exclusive selection)时,使用单选按钮;如果选项较多、空间有限,则应改用下拉框(dropdown),因为下拉框比平铺展示所有选项更节省空间。

在 react-toolbox 中,单选按钮必须配合RadioGroup一起使用——单个 RadioButton 无法独立表达"一组互斥选项"的语义。RadioGroup负责维护当前选中值,并把checked、disabled、onChange等状态自动派发给内部每个 RadioButton。

主题注入键:文档给出的主题 key 为ToolboxButton(原文档如此描述);在源码层面,组件实际通过themr(RADIO, theme)注册主题,RADIO常量定义于 components/identifiers.js,默认主题样式来自 components/radio/theme.module.css。

二、快速开始:一个完整可运行的示例

从react-toolbox/lib/radio导入RadioGroup与RadioButton,然后像使用任何受控组件一样维护选中值。以下示例直接取自 components/radio/readme.md:

import { RadioGroup, RadioButton } from 'react-toolbox/lib/radio'; class RadioTest extends React.Component { state = { value: 'vvendetta' }; handleChange = (value) => { this.setState({value}); }; render () { return ( <RadioGroup name='comic' value={this.state.value} onChange={this.handleChange}> <RadioButton label='The Walking Dead' value='thewalkingdead'/> <RadioButton label='From Hell' value='fromhell' disabled/> <RadioButton label='V for a Vendetta' value='vvendetta'/> <RadioButton label='Watchmen' value='watchmen'/> </RadioGroup> ); } }

要点解析:

  • RadioGroup的value是当前选中值,onChange在选中值变化时回调(回调参数为被选中 RadioButton 的value);
  • 每个RadioButton通过value标识自身,label为显示文本,disabled可使单个选项禁用;
  • 示例中V for a Vendetta的value为vvendetta,与初始state.value一致,因此默认处于选中状态。

仓库 spec/components/radio.js(以及 TypeScript 版本 spec/ts/radio.tsx)中还有一个更完整的演示,额外展示了onFocus/onBlur的用法:

<RadioGroup name="comic" value={this.state.value} onChange={this.handleChange}> <RadioButton label="The Walking Dead" value="thewalkingdead" /> <RadioButton label="From Hell" value="fromhell" disabled /> <RadioButton label="V for a Vendetta" value="vvendetta" onFocus={this.handleFocus} /> <RadioButton label="Watchmen" value="watchmen" onBlur={this.handleBlur} /> </RadioGroup>

三、RadioGroup API:容器如何管理选中状态

RadioGroup是单选组的容器。它接收的属性和事件会被有选择地派发给子级,但每个 RadioButton 也可以独立声明自己的行为。

3.1 属性总览(来自原文档)

NameTypeDefaultDescription
classNameString''为组添加类,用于自定义样式。
disabledBooleanfalse为 true 时,整个组以禁用状态显示。
nameString输入元素组的 name。
onChangeFunction值变化时被调用的回调函数。
valueAny单选组中默认选中的值。

3.2 源码实现:状态如何下发给子级

在 components/radio/RadioGroup.js 中,renderRadioButtons()使用React.Children.map遍历子元素,并通过isComponentOfType(来自 components/utils/is-component-of-type.js)判断子级是否为 RadioButton,只对真正的 RadioButton 注入派生属性:

React.Children.map(this.props.children, child => ( !isComponentOfType(RadioButton, child) ? child : React.cloneElement(child, { checked: child.props.value === this.props.value, disabled: this.props.disabled || child.props.disabled, onChange: this.handleChange.bind(this, child.props.value), }) ))

由此可以推断出三条关键规则:

  1. checked完全由组决定:child.props.value === this.props.value,选中逻辑与每个按钮自身的checked属性无关;
  2. disabled是"或"关系:组禁用或按钮自身禁用,任一为 true 即禁用;
  3. onChange被包装:点击某个按钮触发的是handleChange,它把该按钮的value作为第一个参数回调this.props.onChange(value, event),这正是示例中handleChange = (value) => ...能直接拿到值的原因。

非 RadioButton 的子元素(如普通节点)会被原样保留,因此你可以在组内混排说明文字等自定义内容。

3.3 组级禁用

RadioGroup的默认disabled为false。设置为true后,所有子按钮均显示为禁用态(CSS 层面由disabled主题类控制,见第五节)。

四、RadioButton API:单个选项的行为与事件

RadioButton是构成单选组的内部组件,渲染为 HTML<input type="radio">,与它相关的属性会透传给该 input 元素。

4.1 属性总览(来自原文档)

NameTypeDefaultDescription
checkedBooleanfalse为 true 时,input 元素默认被选中。由父级传递。
classNameString''为单选按钮添加类,用于自定义样式。
disabledBooleanfalse为 true 时,该项以禁用状态显示。
labelStringornode''单选按钮的标签文本。
nameStringinput 元素的 name。
onBlurFunctioninput 失焦时被调用的回调函数。
onChangeFunction值变化时被调用的回调函数。
onFocusFunctioninput 聚焦时被调用的回调函数。
valueAny单选按钮的值。

4.2 源码实现:点击、聚焦与渲染逻辑

components/radio/RadioButton.js 是核心实现,有几个值得注意的细节:

点击事件处理。组件通过handleClick接管点击逻辑:

handleClick = (event) => { const { checked, disabled, onChange } = this.props; if (event.pageX !== 0 && event.pageY !== 0) this.blur(); if (!disabled && !checked && onChange) onChange(event, this); };

它做了两件事:当点击来自真实鼠标(pageX/pageY非 0)时先调用blur()移除焦点光环,然后再在"未禁用且未选中且有回调"的前提下触发onChange(event, this)——注意回调的第二个参数是组件实例本身。

聚焦/失焦代理。focus()与blur()通过ref挂载的inputNode代理到底层 input 元素,从而让onFocus/onBlur能作用在真实的原生 input 上。

渲染结构。render 输出如下 DOM 结构:

<label>const factory = (ripple) => { const Radio = ({ checked, onMouseDown, theme, ...other }) => ( <div >const ThemedRadio = radioFactory(themedRippleFactory({ centered: true, spread: 2.6 })); const ThemedRadioButton = themr(RADIO, theme)(radioButtonFactory(ThemedRadio)); const ThemedRadioGroup = themr(RADIO, theme)(radioGroupFactory(ThemedRadioButton));
  • radioFactory:生成圆点 DOM;
  • themedRippleFactory({ centered: true, spread: 2.6 }):来自 components/ripple,为圆点注入居中、扩散系数为 2.6 的波纹效果;
  • radioButtonFactory:把圆点与原生 input、label 组装成完整按钮;
  • radioGroupFactory:把多个按钮组装成互斥组;
  • 最外层themr(RADIO, theme):react-css-themr 的注入函数,把 components/radio/theme.module.css 的类名映射到themeprop。

由此可知完整事件链路为:用户点击 label → 原生 input 的 onClick 触发 handleClick → 若可选中则回调 onChange → RadioGroup 的 handleChange 把该按钮 value 上报给父级 → 父级 setState 更新 group 的 value → cloneElement 重新计算 checked 并触发重渲染。这也是它天然受控的原因。

六、主题定制:Theming 类名与 CSS 结构

react-toolbox 采用 CSS Modules + react-css-themr 实现主题化。通过给themeprop 传入类名映射即可替换任意内部样式。

6.1 可注入的主题类(来自原文档)

NameDescription
disabled单选按钮禁用时添加到根节点。
field作为组件的根类使用。
input用于 input 元素。
radio用于单选圆点元素。
radioChecked单选圆点处于选中状态时使用。
ripple为波纹效果提供样式。
text用于文本标签元素。

对应 TypeScript 声明见 components/radio/RadioButton.d.ts(RadioButtonTheme)与 components/radio/base.d.ts(RadioTheme:radio、radioChecked、ripple)。

6.2 默认主题的视觉实现(源码视角)

components/radio/theme.module.css 展示了单选圆点的绘制思路,主题定制时可以此为参考:

  • .radio:通过border: calc(0.2 * var(--unit)) solid var(--radio-text-color)与border-radius: 50%画出外圈;内部圆点由::before伪元素实现,默认transform: scale(0)(不可见),并通过transition: transform 0.2s var(--animation-curve-default)平滑过渡;
  • .radioChecked:composes: radio继承外圈样式,同时border-color切换为主题色var(--radio-inner-color),::before通过transform: scale(0.65)显示内部实心圆点;
  • .ripple:波纹颜色为主题色、透明度 0.3,过渡时长 650ms;
  • .disabled:文字、圆点边框、选中圆点内部均切换为禁用色var(--radio-disabled-color),且cursor: auto;
  • .input:原生 input 被绝对定位、尺寸为 0、opacity: 0完全隐藏,但保留了:focus ~ .radio/:focus ~ .radioChecked的兄弟选择器,从而在键盘聚焦时用box-shadow绘制聚焦光环(无障碍友好)。

6.3 可调样式变量

主题变量集中在 components/radio/config.module.css,定制尺寸与配色时可以直接覆盖这些 CSS 变量:

变量默认值含义
--radio-field-margin-bottomcalc(1.5 * var(--unit))每个选项底边距
--radio-button-sizecalc(2 * var(--unit))圆点直径
--radio-inner-colorvar(--color-primary)选中圆点/内点主题色
--radio-focus-colorcolor-mod(var(--color-black) a(10%))未选中聚焦光环色
--radio-checked-focus-colorcolor-mod(var(--color-primary) a(26%))选中聚焦光环色
--radio-text-colorvar(--color-black)标签文字颜色
--radio-disabled-colorcolor-mod(var(--color-black) a(26%))禁用态颜色
--radio-text-font-sizecalc(1.4 * var(--unit))标签文字字号

七、TypeScript 支持

组件附带了完整的类型声明:

  • components/radio/index.d.ts:模块入口,默认导出RadioButton,并重新导出RadioGroup及相关类型;
  • components/radio/RadioButton.d.ts:RadioButtonProps(含checked、disabled、label、name、onBlur、onChange、onFocus、value、theme)与RadioButtonTheme;
  • components/radio/RadioGroup.d.ts:RadioGroupProps(含children、disabled、name、onChange、value)。

在 TypeScript 项目中可以直接获得属性提示与类型校验;spec/ts/radio.tsx 提供了可参考的 TS 用法示例。

八、小结

react-toolbox 的 RadioButton 是一组"受控容器 + 独立选项"的经典组合:

  • 使用上,牢记RadioGroup的value/onChange是状态中枢,checked完全由组计算得出,按钮自身的checked属性实际上由父级传递;
  • 结构上,按钮由隐藏的原生 input、CSS 绘制的圆点、文本标签与 ripple 波纹叠加而成,天然具备键盘聚焦与无障碍支持;
  • 主题上,通过 react-css-themr 注入field、radio、radioChecked、ripple、disabled、text、input等类名即可深度定制,配合 components/radio/config.module.css 的变量可实现尺寸与配色的一体化调整。

对照 spec/components/radio.js 中的演示,你可以直接在其基础上验证受控切换、禁用态与焦点事件的全部行为。

  • 前端
  • UI组件

【免费下载链接】react-toolbox

A set of React components implementing Google's Material Design specification with the power of CSS Modules

项目地址:https://gitcode.com/gh_mirrors/re/react-toolbox
点击查看免费下载
上一篇:ONNX GraphSurgeon 节点删除实战:重连图结构并用 cleanup 完成清理
下一篇:企业级部署实践:基于vLLM高效运行DeepSeek V2 Lite大模型全指南

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 3:29:13

C语言结构体与内存对齐:sizeof结果为何不是成员大小之和

不少刚学C语言的朋友问过我一个很经典的问题&#xff1a;结构体我大概能看懂&#xff0c;但“内存对齐”这四个字老有人提&#xff0c;这到底是个啥&#xff1f;为什么我明明定义了一个 char 一个 int&#xff0c;sizeof 算出来却不是 5&#xff1f;今天这篇文章就把这两件事一…

作者头像 李华
网站建设 2026/9/25 3:28:16

TREG_TELEMETRY=0关闭treg遥测:隐私设置与数据收集说明

TREG_TELEMETRY0关闭treg遥测&#xff1a;隐私设置与数据收集说明 【免费下载链接】treg OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn 项目地址: https://gitcode.com/GitHub_Trending/treg/treg treg&#xff08;tools-registry&…

作者头像 李华
网站建设 2026/9/25 3:28:12

本地任务消息组件:让数据库事务与外部消息推送(HTTP/RabbitMQ)达成最终一致性的通用组件方案

文档教程后端 【免费下载链接】CodeGuide :books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总&#xff0c;旨在为大家提供一个清晰详细的学习教程&#xff0c;侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助&#xff0c;请给予支持(关注、…

作者头像 李华
网站建设 2026/9/25 3:27:36

SpringBoot容器内存调优:从OOM Killed到全链路排查

先说一个我踩了很久才想明白的坑。之前把一个SpringBoot订单服务部署到HoRain云托管的Kubernetes集群上&#xff0c;内存limit给了2G&#xff0c;当时觉得这个量绰绰有余。结果跑了大概三周&#xff0c;容器开始反复重启。我第一时间去翻应用日志&#xff0c;一个OutOfMemoryEr…

作者头像 李华