React+TypeScript多选组件按选中数量禁用按钮的实现问题
问题:React多选组件的条件禁用逻辑实现
我正在开发一个多选组件,该组件接收选项数量、答案数量、回调函数作为props,用于获取用户选中的选项数据,点击按钮时会显示勾选标记。
组件需满足以下逻辑:
- 当答案数量为1(单选):用户点击任一按钮后,其余按钮需禁用;若再次点击该选中按钮,其余按钮恢复可用。
- 当答案数量大于1(多选):用户选中的按钮数量达到指定答案数后,其余按钮禁用;若取消选中任一已选按钮,其余按钮恢复可用。
附上现有组件代码:
父组件代码
import MultipleChoice from 'components/MultipleChoice'; const ParentComponent = () => { const [curSelectedOptions, setCurSelectedOptions] = useState<number[]>([]); return ( <MultipleChoice numOfOptions={5} numOfAnswers={1} onClick={(selectedOption) => { setCurSelectedOptions((prev) => prev.includes(selectedOption) ? prev.filter((option) => option !== selectedOption) : [...prev, selectedOption] ); }} /> ); };
子组件(MultipleChoice)代码
import { useState } from 'react'; import styles from './MultipleChoice.module.scss'; import _ from 'lodash'; import { CheckOutlined } from '@ant-design/icons'; interface IMultipleChoice { numOfOptions: number; numOfAnswers: number; onClick: (selectedOption: number) => void; } const MultipleChoice: React.FC<IMultipleChoice> = ({ numOfOptions, numOfAnswers, onClick, }) => { const [checkedOption, setCheckedOption] = useState<any>({}); const onHandleClick = (index: any) => { setCheckedOption((prev: any) => ({ ...prev, [index]: !prev[index] })); } return ( <div className={styles.container}> {numOfOptions && _.range(1, numOfOptions + 1).map((option, index) => { return ( <button key={option} value={option} onClick={(e) => { onHandleClick(e, index); // 参数传递错误 onClick(e.currentTarget.value); }} // disabled={} 待实现禁用逻辑 className={`${styles.choiceButton} ${styles.center}`} > <div className={`${styles.circleOfNumber} ${styles.center}`} > <span className={styles.numberText}>{option}</span> {checkedOption[index] ? ( // 控制勾选标记显示 <div className={styles.checkMarkWrapper}> <CheckOutlined style={{ fontSize: 68, color: 'red', }} /> </div> ) : null} </div> </button> ); })} </div> ); };
现有状态说明
checkedOption状态表现:点击5个选项中的1、2按钮,状态为{0: true, 1: true};再次点击相同按钮时,状态变为{0: false, 1: false}而非{}。勾选标记在checkedOption对应index值为true时显示。
目前无法结合现有勾选标记逻辑实现按钮的条件禁用,该如何解决?
解决方案
1. 修正点击事件参数错误
onHandleClick函数需要接收index,原代码传递了事件对象e,需修正为:
onClick={(e) => { onHandleClick(index); onClick(Number(e.currentTarget.value)); // 把字符串value转为数字更严谨 }}
2. 计算已选中选项数量
新增变量统计当前已选中的选项个数:
const selectedCount = Object.values(checkedOption).filter(isChecked => isChecked).length;
3. 实现条件禁用逻辑
针对disabled属性分场景处理:
- 单选场景(numOfAnswers === 1):已选中1个选项且当前按钮未被选中 → 禁用
- 多选场景(numOfAnswers > 1):已选数量达标且当前按钮未被选中 → 禁用
最终disabled条件:
disabled={ (numOfAnswers === 1 && selectedCount === 1 && !checkedOption[index]) || (numOfAnswers > 1 && selectedCount === numOfAnswers && !checkedOption[index]) }
4. 完整修改后的MultipleChoice组件
import { useState } from 'react'; import styles from './MultipleChoice.module.scss'; import _ from 'lodash'; import { CheckOutlined } from '@ant-design/icons'; interface IMultipleChoice { numOfOptions: number; numOfAnswers: number; onClick: (selectedOption: number) => void; } const MultipleChoice: React.FC<IMultipleChoice> = ({ numOfOptions, numOfAnswers, onClick, }) => { const [checkedOption, setCheckedOption] = useState<Record<number, boolean>>({}); const onHandleClick = (index: number) => { setCheckedOption((prev) => ({ ...prev, [index]: !prev[index] })); }; // 统计已选中的选项数量 const selectedCount = Object.values(checkedOption).filter(isChecked => isChecked).length; return ( <div className={styles.container}> {numOfOptions && _.range(1, numOfOptions + 1).map((option, index) => { const isChecked = checkedOption[index] || false; return ( <button key={option} value={option} onClick={(e) => { onHandleClick(index); onClick(Number(e.currentTarget.value)); }} disabled={ (numOfAnswers === 1 && selectedCount === 1 && !isChecked) || (numOfAnswers > 1 && selectedCount === numOfAnswers && !isChecked) } className={`${styles.choiceButton} ${styles.center}`} > <div className={`${styles.circleOfNumber} ${styles.center}`}> <span className={styles.numberText}>{option}</span> {isChecked ? ( <div className={styles.checkMarkWrapper}> <CheckOutlined style={{ fontSize: 68, color: 'red', }} /> </div> ) : null} </div> </button> ); })} </div> ); }; export default MultipleChoice;
逻辑验证
- 单选场景:点击任意按钮后其余按钮禁用,再次点击该按钮取消选中,其余按钮恢复可用。
- 多选场景:选中数量达标后未选中按钮禁用,取消任意已选按钮后,未选中按钮恢复可用。
内容的提问来源于stack exchange,提问作者Dan0328
相关产品推荐
相关产品推荐

