You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法使用name属性时如何实现自定义单选按钮组件的分组功能

单选按钮分组实现方案

单选分组的互斥逻辑应该在RadioGroup组件层级实现,这是最符合逻辑分层的通用方案:单个Radio只负责自身的样式和交互触发,同组的状态统一由上层RadioGroup管理,天然就实现了选中互斥的效果。

具体实现步骤

1. 实现RadioGroup组件,用Context共享组内状态

RadioGroup支持受控/非受控两种模式:

  • 受控模式:用户传入value和onChange,由用户外部管理选中状态
  • 非受控模式:组件内部维护选中状态,支持传入defaultValue设置初始值
    代码示例:
import { createContext, useContext, useState, FC } from 'react';

// 定义RadioGroup的Context类型
type RadioGroupContextType = {
  currentValue: any;
  disabled: boolean;
  onChange: (value: any) => void;
}
const RadioGroupContext = createContext<RadioGroupContextType | null>(null);

// RadioGroup组件props定义
type RadioGroupProps = {
  children: React.ReactNode;
  value?: any;
  defaultValue?: any;
  disabled?: boolean;
  onChange?: (value: any) => void;
}

export const RadioGroup: FC<RadioGroupProps> = ({
  children,
  value,
  defaultValue,
  disabled = false,
  onChange
}) => {
  // 非受控模式下内部维护状态
  const [innerValue, setInnerValue] = useState(defaultValue);
  // 优先用外部传入的受控value
  const currentValue = value !== undefined ? value : innerValue;

  const handleChange = (newValue: any) => {
    // 非受控模式更新内部状态
    if (value === undefined) {
      setInnerValue(newValue);
    }
    // 触发外部回调
    onChange?.(newValue);
  }

  return (
    <RadioGroupContext.Provider value={{ currentValue, disabled, onChange: handleChange }}>
      <div className="radio-group" style={{ display: 'flex', gap: '16px' }}>
        {children}
      </div>
    </RadioGroupContext.Provider>
  )
}

// 封装Context获取hook,方便Radio调用
export const useRadioGroup = () => {
  return useContext(RadioGroupContext);
}

2. 改造现有Radio组件,对接Group的Context

不需要再让用户手动给每个Radio传checked属性,自动从Context里取状态对比即可:

// 原有类型定义保持不变
type RadioProps = {
  checked?:  boolean;
  children?: any;
  disabled?: boolean;
  label?:    string;
  onClick?:  (value?: any) => void;
  size?:     RadioSize;
  value?:    any;
};

export const Radio: FC<RadioProps> = ({
  disabled = false,
  onClick,
  label = "",
  value,
  size = "small",
  ...rest
}) => {
  // 获取同组的共享状态
  const groupContext = useRadioGroup();
  // 如果在Group内,优先用Group的状态和配置
  const checked = groupContext ? groupContext.currentValue === value : rest.checked;
  const isDisabled = groupContext ? (groupContext.disabled || disabled) : disabled;

  const handleClick = () => {
    if (isDisabled) return;
    // 触发Group的状态更新
    groupContext?.onChange(value);
    // 保留原有自定义点击回调
    onClick?.(value);
  }

  return (
    // 注意:原代码中Radio内部嵌套RadioGroup是错误写法,替换为普通容器即可
    <div onClick={handleClick} style={{ display: 'flex', alignItems: 'center', cursor: isDisabled ? 'not-allowed' : 'pointer' }}>
      <StyledRadio
        {...rest}
        disabled = {isDisabled}
        size     = {size}
        checked  = {checked}
      />
      <RadioLabel size={size}>{label}</RadioLabel>
    </div>
  );
};

3. 实际使用示例

// 非受控模式,组件内部维护状态
<RadioGroup defaultValue="option1" onChange={(val) => console.log('选中了', val)}>
  <Radio label="选项1" value="option1" />
  <Radio label="选项2" value="option2" />
  <Radio label="选项3" value="option3" disabled />
</RadioGroup>

// 受控模式,外部管理状态
const [selected, setSelected] = useState('option2');
<RadioGroup value={selected} onChange={setSelected}>
  <Radio label="选项1" value="option1" />
  <Radio label="选项2" value="option2" />
</RadioGroup>

方案优势

  • 逻辑分层清晰,单个Radio不需要关心组内其他组件的状态,所有互斥逻辑统一在Group层处理
  • 支持嵌套分组,不同RadioGroup之间互不干扰
  • 兼容受控/非受控两种使用模式,覆盖大部分业务场景
  • 天然支持全局禁用,只需要给RadioGroup加disabled属性,组内所有Radio都会自动生效

内容的提问来源于stack exchange,提问作者agobago

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 01:06:03