React子组件向父组件指定数组传值异常问题求助
解决React答题应用的选中答案同步异常问题
问题描述
开发React答题应用时,点击子组件Quiz的答案按钮出现两个异常:
- 点击按钮时,选中的答案会同步到所有问题的数组项中
- 点击其他答案按钮时,所有问题的已选中答案都会被新答案替换
核心原因分析
- 缺少问题唯一标识:opentdb API返回的问题数据没有
id字段,代码中错误使用了不存在的quiz.id作为匹配标识,导致data.id === questionId始终为true(两者都是undefined),更新状态时所有问题都会被修改。 - 组件key属性错误:Quiz组件的
key使用了quiz.question,若存在重复问题(或触发React组件复用逻辑),会导致组件状态混乱。 - 答案洗牌逻辑错误:原代码未将正确答案正确加入洗牌后的答案数组,同时
useEffect依赖项缺失,导致洗牌逻辑无法响应props变化。
解决方案步骤
1. 为问题添加唯一标识
在获取API数据时,为每个问题添加唯一id字段(可使用索引或UUID),确保能精准定位单个问题。
2. 修正组件key属性
将Quiz组件的key改为问题的唯一id,避免React复用组件导致的状态异常。
3. 修复答案洗牌逻辑
调整洗牌代码,确保正确答案被加入答案列表,并补全useEffect的依赖项,保证逻辑能正确触发。
4. 确保选中答案的精准更新
通过唯一id匹配目标问题,只更新对应问题的selectedAnswer字段。
修改后的代码
App.js(父组件)
import React from 'react'; import './App.css'; import Start from './components/Start'; import Quiz from './components/Quiz'; import { useState } from 'react'; function App() { const [isStartScreenShowing, setIsStartScreenShowing] = useState(true) const [questions, setQuestions] = useState([]) const apiURL = 'https://opentdb.com/api.php?amount=5&category=9&difficulty=easy&type=multiple' function toggleStartScreen() { setIsStartScreenShowing(false) fetchData() } async function fetchData() { const response = await fetch(apiURL) const data = await response.json() // 为每个问题添加唯一id const questionsWithId = data.results.map((question, index) => ({ ...question, id: index // 也可以用uuid生成更唯一的标识 })) setQuestions(questionsWithId) } function selectAnswer(questionId, answer) { setQuestions((prevQuestions) => prevQuestions.map((question) => question.id === questionId ? { ...question, selectedAnswer: answer } : question ) ); } const quizElements = questions.map(quiz => ( <Quiz question={quiz.question} key={quiz.id} // 使用唯一id作为key correctAnswer={quiz.correct_answer} incorrectAnswers={quiz.incorrect_answers} selectAnswer={(answer) => selectAnswer(quiz.id, answer)} /> )) function handleClick() { console.log(questions) } const checkAnswers = <button onClick={handleClick}>Check Answers</button> return ( <div className="App"> {isStartScreenShowing && <Start isStartScreenShowing={isStartScreenShowing} toggleStartScreen={toggleStartScreen} /> } {!isStartScreenShowing && <> {quizElements} {checkAnswers} </> } </div> ); } export default App;
Quiz.js(子组件)
import React from "react"; import { useState, useEffect } from "react"; import './style.css' const he = require('he'); function Quiz(props) { const [shuffledAnswers, setShuffledAnswers] = useState([]) useEffect(()=>{ // 直接操作shuffled数组,加入正确答案 const shuffled = [...props.incorrectAnswers] const ranNumForAnswerMix = Math.floor(Math.random() * (shuffled.length + 1)) shuffled.splice(ranNumForAnswerMix, 0, props.correctAnswer) setShuffledAnswers(shuffled) }, [props.incorrectAnswers, props.correctAnswer]) // 添加依赖项 const decodedQuestion = typeof props.question === 'string' ? he.decode(props.question) : props.question; const decodedCorrectAnswer = typeof props.correctAnswer === 'string' ? he.decode(props.correctAnswer) : props.correctAnswer; const decodedAnswers = shuffledAnswers.map(answer => typeof answer === 'string' ? he.decode(answer) : answer ); const answerButtons = decodedAnswers.map(decodedAnswer => ( <button id={decodedAnswer} key={decodedAnswer} onClick={()=>props.selectAnswer(decodedAnswer)} > {decodedAnswer} </button> )) return ( <> <div className="quiz-parent"> <div> <div> <p>{decodedQuestion}</p> <p>正确答案: {decodedCorrectAnswer}</p> {answerButtons} </div> </div> </div> </> ) } export default Quiz
验证效果
修改完成后:
- 点击单个问题的答案按钮,只有该问题的
selectedAnswer会被更新 - 点击其他问题的答案,不会影响已选中的其他问题答案
- 答案列表会正确包含正确答案并随机排序
内容的提问来源于stack exchange,提问作者Hiran Kaushal
相关产品推荐
相关产品推荐

