React实现单选组件的N种方式
直接用通过props + options/cloneElement + children
RadioGroup
1 | function RadioGroup (props) { |
Radio
1 | function Radio (props) { |
使用方法
1 | const options = [ |
优点
1.显式数据流
- 通过 options 或 children 直接控制子组件,数据来源清晰可见
- 每个Radio 的 checked 和 onChange 由父组件显式注入,父子关系一目了然
2.灵活性高
- 同时支持 options 配置和 children 动态插入两种模式
- 可通过cloneElement 动态扩展子组件功能(如添加额外 Props)
3.组件复用性
- Radio组件不依赖父级逻辑,可独立复用(如单独使用一个 Radio 按钮)
缺点
1.侵入性强
- 使用cloneElement 会覆盖子组件原有 Props,可能引发命名冲突(如子组件已有 checked 属性)
- 需要手动处理React.Children.map 和 isValidElement,代码复杂度较高
2.性能隐患
- 每次渲染都会重新克隆子元素,可能触发不必要的子组件更新
- 对大型列表(如 1000+ 个Radio)性能较差
3.类型丢失
- 克隆后的子组件可能丢失 TypeScript 类型提示,需手动声明合并后的 Props 类型‘
通过context + options/children的方式
context
1 | const RadioGroupContext = createContext(null); |
RadioGroup
1 | function RadioGroup (props) { |
Radio
1 | function Radio (props) { |
使用方法
1 | const options = [ |
优点
1.跨层级通信
- 天然支持深层嵌套组件(如RadioGroup -> Wrapper -> Radio)
- 子组件无需通过中间层传递 Props
2.代码简洁
- 消除 Props 逐层传递的冗余代码
- 子组件通过useContext 直接消费状态,逻辑集中管理
3.动态扩展
- 支持动态插入子组件(如根据条件渲染不同的Radio)
- 对options 和 children 两种模式统一处理
缺点
1.组件耦合
- Radio必须存在于 RadioGroup 上下文中,无法独立使用
- 脱离上下文的Radio 会直接报错(需额外处理默认值)
2.调试困难
- Context 数据流在 DevTools 中不如 Props 直观
- 难以追踪状态变化的来源(尤其是多层 Context 嵌套时)
3.性能优化复杂
- Context 变化会触发所有消费组件的重渲染
- 需配合memo 或 useMemo 手动优化