React Quiz应用checkAnswers函数异常:始终返回首个答案而非用户选择项
问题分析:选择任意答案始终只显示第一个答案的原因及修复
现象
无论点击哪个选项,界面上始终只有第一个答案呈现选中状态,同时点击「Check Answers」后也无法正确统计得分。
问题根源
1. 单选逻辑缺失
holdAnswer函数仅切换当前点击选项的isChosen状态,但没有取消同一问题下其他选项的选中标记。这会导致多个选项同时被标记为选中,而界面渲染时可能因为样式判断逻辑,只呈现第一个选中项的状态。
2. 得分统计逻辑完全错误
原checkAnswers里的查找代码answer.answer.id === answer.answer.isChosen完全不符合逻辑:
answer.answer是字符串类型的答案文本,根本不存在id和isChosen属性- 正确逻辑应该是筛选出
isChosen为true的选项
3. 列表渲染的Key值错误
渲染问题列表时,外层<section>使用index作为key,而非问题的唯一ID。当问题状态更新时,React可能会复用旧DOM节点,导致状态渲染异常。
修复方案
1. 完善单选逻辑
在holdAnswer中,更新问题答案时,将当前点击的选项设为选中,同时把其他选项的选中状态置为false,确保同一问题下只有一个选项被选中。
2. 修正得分统计逻辑
遍历问题时,正确筛选出isChosen为true的选项,再判断其是否为正确答案,最后统一更新分数(避免循环内多次调用setState)。
3. 使用唯一Key值
将问题列表外层<section>的key改为question.id,确保React能正确识别每个问题节点。
额外优化
添加答案顺序打乱逻辑,避免正确答案固定在最后一个位置。
修复后的完整代码
App.js
import { useState } from "react"; import QuestionSet from "./components/QuestionSet"; import Answers from "./components/Answers"; import { nanoid } from "nanoid"; function App() { const [isQuesLoaded, setIsQuesLoaded] = useState(false); const [questions, setQuestions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [score, setScore] = useState(0); async function startQuiz() { try { setIsQuesLoaded(true); const response = await fetch( "https://opentdb.com/api.php?amount=5&category=12&difficulty=easy&type=multiple" ); const data = await response.json(); const allQuestions = data.results; const listOfQuestions = allQuestions.map((item) => { const allAnswers = [ { id: nanoid(), isCorrect: false, isChosen: false, answer: item.incorrect_answers[0] }, { id: nanoid(), isCorrect: false, isChosen: false, answer: item.incorrect_answers[1] }, { id: nanoid(), isCorrect: false, isChosen: false, answer: item.incorrect_answers[2] }, { id: nanoid(), isCorrect: true, isChosen: false, answer: item.correct_answer } ]; // 打乱答案顺序,避免正确答案固定在最后 allAnswers.sort(() => Math.random() - 0.5); return { id: nanoid(), question: item.question, answers: allAnswers }; }); setQuestions(listOfQuestions); } catch (err) { setError(err.message); } finally { setLoading(false); } } function holdAnswer(questionId, answerId) { setQuestions((prevQuestion) => prevQuestion.map((question) => question.id === questionId ? { ...question, answers: question.answers.map((answer) => answer.id === answerId ? { ...answer, isChosen: true } : { ...answer, isChosen: false } // 取消其他选项的选中状态 ) } : question ) ); } function checkAnswers() { let correctCount = 0; questions.forEach(question => { const selectedAnswer = question.answers.find(answer => answer.isChosen); if (selectedAnswer && selectedAnswer.isCorrect) { correctCount += 1; } }); setScore(correctCount); // 统一更新分数,避免循环内多次setState } const questionElm = questions.map((question) => { return ( <section key={question.id}> {/* 使用问题唯一ID作为key */} <QuestionSet question={question.question} /> <Answers answers={question.answers} holdAnswer={(answerId) => holdAnswer(question.id, answerId)} /> </section> ); }); return ( <div className="App"> {!isQuesLoaded ? ( <main> <h1 className="title-app">Quizzical</h1> <p className="desc-app">Some description if needed</p> <button className="btn" onClick={startQuiz}> Start Quiz </button> </main> ) : ( <main className="quest-box"> {loading && <div>Loading data...</div>} {error && <div>{`There is a problem fetchning data = ${error}`}</div>} <section className="quest-content">{questionElm}</section> <button className="answer-btn" onClick={checkAnswers}>Check Answers</button> </main> )} </div> ); } export default App;
Answers.js
function Answer(props) { const styles = { backgroundColor: props.answer.isChosen ? "#D6DBF5" : "transparent" }; return ( <div className="answer-div" style={styles} onClick={() => props.holdAnswer(props.answer.id)} > <p>{props.answer.answer}</p> </div> ); } export default function Answers(props) { return ( <section className="answer-container"> {props.answers.map((answer) => ( <Answer holdAnswer={props.holdAnswer} answer={answer} key={answer.id} /> ))} </section> ); }
QuestionSet.js
export default function QuestionSet(props) { return ( <section className="quest"> <p>{props.question}</p> </section> ); }
内容的提问来源于stack exchange,提问作者lsokol
相关产品推荐
相关产品推荐

