React.js测验应用中如何保存单选按钮选中状态
问题分析与解决方案
你的核心问题在于直接操作DOM控制单选框选中状态,这和React状态驱动UI的理念冲突,加上状态更新的异步特性,导致选中状态混乱。下面是修复方案,完全用React状态管理实现单选框的状态保持:
关键问题点
- 用
document.querySelectorAll手动设置checked属性,会和React受控组件机制冲突,造成状态不同步。 setCurrent是异步操作,handlePrevious中访问current时仍是旧值,导致读取错误的题目答案。handlerSave中用push存储答案到localStorage,会导致重复存储,无法正确对应题目索引。
修复后的完整代码
import React, { useState, useEffect } from 'react' import Result from './Result' import './Exam.css' function Exam() { const data = JSON.parse(localStorage.getItem('Que')) || [] const savedAnswers = JSON.parse(localStorage.getItem('Ans')) || [] // 初始化用户答案数组,长度匹配题目数量,缺省值为空字符串 const [userAnswers, setUserAnswers] = useState(() => Array(data.length).fill('').map((_, idx) => savedAnswers[idx] || '') ) const [current, setCurrent] = useState(0) const [exit, setExit] = useState(false) const [correctCount, setCorrectCount] = useState(0) // 用户答案变化时同步更新到localStorage useEffect(() => { localStorage.setItem('Ans', JSON.stringify(userAnswers)) }, [userAnswers]) // 测验结束时统一计算正确答案数量 useEffect(() => { if (exit) { const count = userAnswers.reduce((total, ans, idx) => { return ans === data[idx]?.answer ? total + 1 : total }, 0) setCorrectCount(count) } }, [exit, userAnswers, data]) const handleAnswerChange = (e) => { const newAnswers = [...userAnswers] newAnswers[current] = e.target.value setUserAnswers(newAnswers) } const handlePrevious = () => { if (current > 0) { setCurrent(current - 1) } } const handleSaveNext = () => { if (current + 1 === data.length) { setExit(true) } else { setCurrent(current + 1) } } if (!data.length) { return <div>No questions found!</div> } return( <div> {!exit ? ( <div className='mcq'> <div> Question :<span className='span'>{current + 1}</span> {data[current].question}? </div><br/> <div> <div className='option'> <input type='radio' value='A' name={`question-${current}`} // 每个题目用独立name,避免单选框组冲突 onChange={handleAnswerChange} checked={userAnswers[current] === 'A'} className='radioButton' /> {data[current].A} </div> <div className='option'> <input type='radio' value='B' name={`question-${current}`} onChange={handleAnswerChange} checked={userAnswers[current] === 'B'} className='radioButton' /> {data[current].B} </div> <div className='option'> <input type='radio' value='C' name={`question-${current}`} onChange={handleAnswerChange} checked={userAnswers[current] === 'C'} className='radioButton' /> {data[current].C} </div> <div className='option'> <input type='radio' value='D' name={`question-${current}`} onChange={handleAnswerChange} checked={userAnswers[current] === 'D'} className='radioButton' /> {data[current].D} </div> </div><br/> <div> {current !== 0 && <button onClick={handlePrevious}>Previous</button>} <button onClick={handleSaveNext}>Save & Next</button> </div> </div> ) : ( <Result props={{correct: correctCount}}/> )} </div> ) } export default Exam;
核心改进说明
- 受控组件实现:每个单选框的
checked属性由userAnswers[current]直接控制,完全由React状态驱动,无需手动操作DOM。 - 独立单选框组:给每个题目设置唯一
name(question-${current}),避免不同题目间的单选框互相干扰。 - 正确答案存储:用数组
userAnswers对应题目索引,同步更新到localStorage,确保每个题目答案位置准确,无重复存储。 - 异步状态处理:移除所有DOM操作,利用React状态更新自动触发UI重渲染,避免异步状态导致的索引错误。
- 统一计算正确数:测验结束时统一统计正确答案数量,避免用户返回修改答案导致的计数偏差。
内容的提问来源于stack exchange,提问作者jenish963
相关产品推荐
相关产品推荐

