如何在 React 中为单选题选项添加点击高亮效果(仅当前题目生效)

1次阅读

如何在 React 中为单选题选项添加点击高亮效果(仅当前题目生效)

本文详解如何在 react 问答应用中,为每个题目独立管理选项的点击状态,实现「点击变色、切换复原」的交互逻辑,避免跨题干扰与选项错位问题。

本文详解如何在 react 问答应用中,为每个题目独立管理选项的点击状态,实现「点击变色、切换复原」的交互逻辑,避免跨题干扰与选项错位问题。

在构建 Quiz 类型的 React 应用时,一个常见且关键的交互需求是: 用户点击某道题的某个选项后,该选项高亮(如背景色变化);当用户再次点击同一题的其他选项时,原高亮项恢复默认样式,新选项接替高亮状态 。但若状态管理不当(例如全局共享 isClickedIndex),就会导致所有题目共用同一索引,引发「一动全动」「选项顺序错乱」等典型 Bug——这正是原始代码的核心问题。

根本原因在于:isClickedIndex 是一个全局 state,它无法区分不同题目;而 createRandomOptions() 在每次渲染时都重新打乱数组,配合未绑定题目标识的事件处理,使得 DOM 元素与数据索引关系失稳。

✅ 正确解法是: 为每道题维护独立的已选答案索引(或值),并通过条件 className 动态控制样式

✅ 推荐实现方案(状态隔离 + 精准渲染)

首先,将 isClickedIndex 升级为按题目索引组织的对象:

const [selectedIndices, setSelectedIndices] = useState<Record<number, number>>({}); // key: question index, value: selected option index within that question

然后,在渲染每道题时,传入其唯一题号(qIndex),并绑定专属点击处理器:

function handleOptionClick(qIndex: number, optIndex: number) {setSelectedIndices(prev => ({     ……prev,     [qIndex]: optIndex   })); }

最后,在 <li> 中通过 selectedIndices[qIndex] === optIndex 判断是否激活:

<li   key={option}   className={`option ${selectedIndices[qIndex] === optIndex ? 'active' : ''}`}   onClick={() => handleOptionClick(qIndex, optIndex)} >   {option} </li>

⚠️ 注意事项:

  • 不要用 index 作为 key:randomOptions 每次重排都会导致 index 变化,React 会错误复用 DOM。应改用稳定标识,如 option(需确保选项内容唯一)或 btoa(option) 做哈希。
  • 避免在 map 内调用 createRandomOptions():应在 useEffect 或 useMemo 中预计算随机顺序,保证渲染一致性。
  • CSS 样式需明确作用域 :推荐使用 CSS Modules 或 BEM 命名,防止 .active 影响全局。

示例 CSS(局部生效):

/* Quiz.module.css */ .option {padding: 12px 16px;   margin: 4px 0;   border-radius: 6px;   cursor: pointer;   transition: background-color 0.2s;} .option.active {background-color: #4f46e5;   color: white;}

? 补充:若需支持「取消选择」(点击已选项即取消)

只需在 handleOptionClick 中增加判断:

function handleOptionClick(qIndex: number, optIndex: number) {setSelectedIndices(prev => {     if (prev[qIndex] === optIndex) {const updated = { ……prev};       delete updated[qIndex];       return updated;     }     return {……prev, [qIndex]: optIndex };   }); }

✅ 总结

问题现象 根本原因 解决关键
所有题同时变色 全局 isClickedIndex 每题独立 state(Record<number, number>)
选项位置跳动 map 内动态打乱 + index key 预计算随机顺序 + 稳定 key
样式不生效 CSS 未正确作用于 .active 检查 CSS 优先级与作用域

通过状态粒度下沉与渲染稳定性保障,即可实现专业、可维护的单题单选高亮体验——这也是 React「状态最小化、作用域最细化」设计哲学的典型实践。

text=ZqhQzanResources