You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React子组件向父组件指定数组传值异常问题求助

解决React答题应用的选中答案同步异常问题

问题描述

开发React答题应用时,点击子组件Quiz的答案按钮出现两个异常:

  • 点击按钮时,选中的答案会同步到所有问题的数组项中
  • 点击其他答案按钮时,所有问题的已选中答案都会被新答案替换

核心原因分析

  1. 缺少问题唯一标识:opentdb API返回的问题数据没有id字段,代码中错误使用了不存在的quiz.id作为匹配标识,导致data.id === questionId始终为true(两者都是undefined),更新状态时所有问题都会被修改。
  2. 组件key属性错误:Quiz组件的key使用了quiz.question,若存在重复问题(或触发React组件复用逻辑),会导致组件状态混乱。
  3. 答案洗牌逻辑错误:原代码未将正确答案正确加入洗牌后的答案数组,同时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 18:55:01