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

React答题应用开发:如何实现选项随机打乱功能?

解决React答题应用中选项无法打乱的问题

问题分析

你的代码里有几个核心问题导致选项无法被正确打乱:

  1. 你实现的Fisher-Yates洗牌函数被注释了,而且就算没注释,也没实际调用它来处理选项数组;
  2. 你尝试的sort(() => 0.5 - Math.random())方式不严谨,这种排序算法的随机性不均匀,且你只是定义了testQuestions但没实际使用;
  3. 题目生成逻辑在组件每次渲染时都会执行,但此时API数据可能还没加载完成,导致namearray和capitalArray为空,同时存在重复选到同一国家的可能;
  4. 选项数组的顺序是硬编码的,没有动态打乱的步骤。

解决方案

  1. 启用并正确调用Fisher-Yates洗牌函数:这个算法是业界公认的高效且公平的洗牌方式,直接复用你之前写的即可;
  2. 将题目生成逻辑放到useEffect中:确保只有在API数据加载完成后才生成题目,避免空值问题;
  3. 优化国家选择逻辑:避免重复选取同一个国家;
  4. 在生成每个问题的选项后立即洗牌:确保每个题目的选项顺序都是随机的。

修改后的完整代码

import React from "react";
import "./Quiz.css";
import { useState, useEffect } from "react";

function Quiz() {
  const [cname, scname] = useState([]);
  const [results, finalres] = useState(false);
  const [score, setScore] = useState(0);
  const [currentQues, setCurrentQues] = useState(0);
  const [questions, setQuestions] = useState([]); // 用state存储题目,避免重复生成

  // Fisher-Yates洗牌函数
  const shuffle = (array) => {
    let currentIndex = array.length;
    let randomIndex;
    while (currentIndex !== 0) {
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex--;
      [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
    }
    return array;
  };

  // 生成不重复的随机国家索引
  const getUniqueRandomIndices = (count, total) => {
    const indices = new Set();
    while (indices.size < count) {
      indices.add(Math.floor(Math.random() * total));
    }
    return Array.from(indices);
  };

  // 获取API数据并生成题目
  useEffect(() => {
    fetch("https://countriesnow.space/api/v0.1/countries/capital")
      .then((response) => response.json())
      .then((data) => {
        scname(data.data);
        // 生成5个不重复的国家索引
        const selectedIndices = getUniqueRandomIndices(5, data.data.length);
        const generatedQuestions = selectedIndices.map((mainIndex) => {
          const mainCountry = data.data[mainIndex];
          // 为当前题目生成3个错误选项(从非当前国家中选)
          const wrongIndices = getUniqueRandomIndices(3, data.data.length).filter(i => i !== mainIndex);
          const answerOptions = [
            { answerText: mainCountry.capital, isCorrect: true },
            ...wrongIndices.map(i => ({ answerText: data.data[i].capital, isCorrect: false }))
          ];
          // 打乱选项顺序
          shuffle(answerOptions);
          return {
            questionText: `What is the capital of ${mainCountry.name}?`,
            answerOptions
          };
        });
        setQuestions(generatedQuestions);
      });
  }, []);

  const restartGame = () => {
    setScore(0);
    setCurrentQues(0);
    finalres(false);
    // 重新生成题目
    const selectedIndices = getUniqueRandomIndices(5, cname.length);
    const generatedQuestions = selectedIndices.map((mainIndex) => {
      const mainCountry = cname[mainIndex];
      const wrongIndices = getUniqueRandomIndices(3, cname.length).filter(i => i !== mainIndex);
      const answerOptions = [
        { answerText: mainCountry.capital, isCorrect: true },
        ...wrongIndices.map(i => ({ answerText: cname[i].capital, isCorrect: false }))
      ];
      shuffle(answerOptions);
      return {
        questionText: `What is the capital of ${mainCountry.name}?`,
        answerOptions
      };
    });
    setQuestions(generatedQuestions);
  };

  const handleAnswerClick = (isCorrect) => {
    if (isCorrect) {
      setScore(prev => prev + 1);
    }
    const nextQues = currentQues + 1;
    if (nextQues < questions.length) {
      setCurrentQues(nextQues);
    } else {
      finalres(true);
    }
  };

  // 数据未加载时显示加载状态
  if (questions.length === 0) {
    return <div>Loading...</div>;
  }

  return (
    <>
      <h4>Quiz</h4>
      <div id="head">Welcome User</div>
      <hr />
      {results ? (
        <div className="final-res">
          Final Results
          <h4>you scored {score} out of 5</h4>
          <button onClick={restartGame}>Restart</button>
        </div>
      ) : (
        <div>
          <div id="quescard">
            <h3>
              {currentQues + 1}. {questions[currentQues].questionText}
            </h3>
            {questions[currentQues].answerOptions.map((ansopt, idx) => (
              <button key={idx} onClick={() => handleAnswerClick(ansopt.isCorrect)}>
                {ansopt.answerText}
              </button>
            ))}
          </div>
        </div>
      )}
    </>
  );
}

export default Quiz;

关键改动说明

  • 新增questions状态存储生成的题目,避免组件每次渲染重复生成;
  • 启用Fisher-Yates洗牌函数,在每个题目生成后立即调用打乱选项;
  • 新增getUniqueRandomIndices函数,确保选取的国家不重复,错误选项不会用到当前题目的正确首都;
  • 将题目生成逻辑移到useEffect中,确保API数据加载完成后再生成题目;
  • 优化restartGame函数,重新生成随机题目并打乱选项;
  • 给选项按钮添加key属性,符合React列表渲染要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 23:45:35