如何正确管理 Shadcn RadioGroup 在多题页面中的单选状态

1次阅读

Shadcn 的 RadioGroup 在分页问卷中出现「前一题答案被错误复用到后一题」的问题,根本原因是未显式控制 RadioGroupItem 的 checked 属性;修复关键在于为每个选项项手动绑定 checked={value === savedAnswer},而非仅依赖 RadioGroup 的 value。

shadcn 的 `radiogroup` 在分页问卷中出现「前一题答案被错误复用到后一题」的问题,根本原因是未显式控制 `radiogroupitem` 的 `checked` 属性;修复关键在于为每个选项项手动绑定 `checked={value === savedanswer}`,而非仅依赖 `radiogroup` 的 `value`。

在基于分页的多选题应用(如在线测验)中,使用 shadcn/ui 的 RadioGroup 组件时,开发者常误以为只要为 RadioGroup 设置 value 属性,其内部的 RadioGroupItem 就会自动同步选中状态。但实际并非如此——RadioGroupItem 是无状态的底层组件,它不会主动读取父级 RadioGroup 的 value 进行渲染判断 。当多个题目共用相同选项文本(例如连续两题均含“Hippo”),且 questionIndex 变更后 RadioGroup 重新挂载或重渲染时,浏览器可能因 DOM 复用或 id 冲突,沿用上一个 RadioGroupItem 的原生 checked 状态,导致「答案残留」现象。

✅ 正确做法:显式声明每个选项的 checked 状态

你需要在渲染每个 RadioGroupItem 时, 显式传入 checked prop,使其严格受当前题目的已保存答案控制:

{props.questionOptions.map((answerText, i) => (<div key={answerText} className="flex items-center space-x-2">     <RadioGroupItem       value={answerText}       // ✅ 关键修复:显式控制 checked 状态       checked={props.savedAnswers[props.questionIndex]?.answer === answerText}       disabled={props.savedAnswers[props.questionIndex]?.checked}       id={`${props.questionIndex}-${answerText}`} // ✨ 建议增强唯一性     />     <Label htmlFor={`${props.questionIndex}-${answerText}`}>{answerText}</Label>   </div> ))}

? 为什么 id 也要加题号前缀?
RadioGroupItem 依赖 id 与 <Label htmlFor> 关联。若不同题目使用相同 answerText(如都含 “Hippo”),不加题号会导致多个 input 共享同一 id,违反 HTML 规范,并可能引发浏览器自动选中逻辑混乱。添加 questionIndex 前缀可确保全局唯一性。

? 补充注意事项

  • RadioGroup 的 value 仍需保留 :它用于触发 onValueChange 回调及表单受控逻辑,不可移除;
  • 避免 key 仅用 answerText:若选项文本重复(如多题均有 “None of the above”),应改用 i 或组合键(如 ${questionIndex}-${i})作为 key,防止 React 列表复用异常;
  • 初始化 savedAnswers:建议在 useState 初始化时预设空对象或合理默认值,避免 undefined?.answer 报错;
  • disabled 逻辑优化建议 :当前 disabled={savedAnswers[…]?.checked} 仅禁用已提交题目的选择,符合业务逻辑;若需允许修改已答题目,可移除此 disabled 或改为条件控制。

✅ 完整受控示例片段

<RadioGroup   value={props.savedAnswers[props.questionIndex]?.answer}   onValueChange={(chosenAnswer) => {props.saveAnswer(chosenAnswer);   }} >   {props.questionOptions.map((answerText, i) => (<div key={`${props.questionIndex}-${i}`} className="flex items-center space-x-2">       <RadioGroupItem         value={answerText}         checked={props.savedAnswers[props.questionIndex]?.answer === answerText}         disabled={props.savedAnswers[props.questionIndex]?.checked}         id={`${props.questionIndex}-${answerText}`}       />       <Label htmlFor={`${props.questionIndex}-${answerText}`}>{answerText}</Label>     </div>   ))} </RadioGroup>

通过以上调整,每道题的单选状态将完全解耦、精确受控,彻底杜绝跨题答案污染问题,同时保持 shadcn 组件的无障碍支持与样式一致性。

text=ZqhQzanResources