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

React组件状态修改问题:答题按钮点击后背景不更新

问题描述

我正在开发一款测验应用,遇到以下问题:

  • 用户点击答案按钮时,无法修改按钮背景色,holdAnswer函数能打印答案ID,但无法修改对应答案的状态
  • 需要存储用户所选答案以统计答题数量,后续还要根据答案对错进行高亮,这些都需要修改组件状态

App.js

import { useState } from "react";
import QuestionSet from "./QuestionSet";
import Answers from "./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);

  async function startQuiz() {
    try {
      setIsQuesLoaded(!isQuesLoaded);
      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,
          },
        ];
        return {
          id: nanoid(),
          question: item.question,
          answers: allAnswers,
        };
      });
      setQuestions(listOfQuestions);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }

  function holdAnswer(id) {
    console.log(id);
    setQuestions((prevQuestion) =>
      prevQuestion.map((question) =>
        question.answers.id === id
          ? {
              ...question,
              answers: question.answers.map((answer) =>
                answer.id === id
                  ? { ...answer, isChosen: !answer.isChosen }
                  : answer
              ),
            }
          : question
      )
    );
  }

  const questionElm = questions.map((question, index) => {
    return (
      <section key={index}>
        <QuestionSet question={question.question} key={question.id} />
        <Answers
          answers={question.answers}
          isChosen={question.answers.isChosen}
          holdAnswer={holdAnswer}
        />
      </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">Check Answers</button>
        </main>
      )}
    </div>
  );
}

export default App;

Answers.js

export default function Answers(props) {
  const styles = {
    backgroundColor: props.answers.isChosen ? "#D6DBF5" : "transparent",
  };

  return (
    <section className="answer-container">
      <div
        className="answer-div"
        style={styles}
        id={props.answers[3].id}
        onClick={() => props.holdAnswer(props.answers[3].id)}
      >
        <p>{props.answers[3].answer}</p>
      </div>
      <div
        className="answer-div"
        style={styles}
        id={props.answers[1].id}
        onClick={() => props.holdAnswer(props.answers[1].id)}
      >
        <p>{props.answers[1].answer}</p>
      </div>
      <div
        className="answer-div"
        style={styles}
        id={props.answers[2].id}
        onClick={() => props.holdAnswer(props.answers[2].id)}
      >
        <p>{props.answers[2].answer}</p>
      </div>
      <div
        className="answer-div"
        style={styles}
        id={props.answers[0].id}
        onClick={() => props.holdAnswer(props.answers[0].id)}
      >
        <p>{props.answers[0].answer}</p>
      </div>
    </section>
  );
}
解决方案

1. 修复holdAnswer状态更新逻辑

原代码中question.answers.id === id判断错误,question.answers是数组,没有id属性。修改为直接遍历每个问题的答案数组,匹配ID切换状态:

function holdAnswer(id) {
  setQuestions((prevQuestions) =>
    prevQuestions.map((question) => ({
      ...question,
      answers: question.answers.map((answer) =>
        answer.id === id ? { ...answer, isChosen: !answer.isChosen } : answer
      )
    }))
  );
}

2. 修复Answers组件样式绑定与渲染逻辑

原代码中props.answers.isChosen无效(数组无此属性),且硬编码答案索引不灵活。改为循环渲染每个答案,绑定对应isChosen状态:

export default function Answers(props) {
  return (
    <section className="answer-container">
      {props.answers.map((answer) => (
        <div
          key={answer.id}
          className="answer-div"
          style={{
            backgroundColor: answer.isChosen ? "#D6DBF5" : "transparent"
          }}
          onClick={() => props.holdAnswer(answer.id)}
        >
          <p>{answer.answer}</p>
        </div>
      ))}
    </section>
  );
}

3. 移除无效props传递

App组件中传递给Answers的isChosen={question.answers.isChosen}无效,直接删除该属性:

<Answers
  answers={question.answers}
  holdAnswer={holdAnswer}
/>

4. 统计答题数量

通过遍历questions数组统计已选择的答案数量:

// 在App组件中添加统计逻辑
const answeredCount = questions.reduce((count, question) => {
  return count + question.answers.filter(answer => answer.isChosen).length;
}, 0);

5. 答案对错高亮

新增状态控制结果显示,点击按钮后切换状态,根据答案的isCorrect和isChosen渲染不同背景色:

  • 在App组件中添加状态:
const [showResults, setShowResults] = useState(false);
  • 修改"Check Answers"按钮:
<button 
  className="answer-btn" 
  onClick={() => setShowResults(true)}
>
  Check Answers
</button>
  • 传递状态给Answers组件:
<Answers
  answers={question.answers}
  holdAnswer={holdAnswer}
  showResults={showResults}
/>
  • 修改Answers组件样式逻辑:
style={{
  backgroundColor: props.showResults 
    ? answer.isCorrect 
      ? "#94D7A2"  // 正确答案绿色
      : answer.isChosen 
        ? "#F8BCBC"  // 选错答案红色
        : "transparent"
    : answer.isChosen 
      ? "#D6DBF5"  // 选中状态蓝色
      : "transparent"
}}

内容的提问来源于stack exchange,提问作者lsokol

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:30:54