React答题应用开发:如何实现选项随机打乱功能?
解决React答题应用中选项无法打乱的问题
问题分析
你的代码里有几个核心问题导致选项无法被正确打乱:
- 你实现的Fisher-Yates洗牌函数被注释了,而且就算没注释,也没实际调用它来处理选项数组;
- 你尝试的
sort(() => 0.5 - Math.random())方式不严谨,这种排序算法的随机性不均匀,且你只是定义了testQuestions但没实际使用; - 题目生成逻辑在组件每次渲染时都会执行,但此时API数据可能还没加载完成,导致
namearray和capitalArray为空,同时存在重复选到同一国家的可能; - 选项数组的顺序是硬编码的,没有动态打乱的步骤。
解决方案
- 启用并正确调用Fisher-Yates洗牌函数:这个算法是业界公认的高效且公平的洗牌方式,直接复用你之前写的即可;
- 将题目生成逻辑放到
useEffect中:确保只有在API数据加载完成后才生成题目,避免空值问题; - 优化国家选择逻辑:避免重复选取同一个国家;
- 在生成每个问题的选项后立即洗牌:确保每个题目的选项顺序都是随机的。
修改后的完整代码
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
相关产品推荐
相关产品推荐

