前提
react×TSでクイズアプリを作っています。
ランダムでクイズを選択肢、それをクイズリストに追加していきたいのですが、上手く追加できずに困っています。
実現したいこと
- setQuizeez();後のsetQuestion()の際に、quizListの値が参照できるようにしたい。
発生している問題・エラーメッセージ
setQuizeez()によって、quizListにクイズが追加されたのち、setQuestion()が実行できるとよいのですが、setQuizList()が非同期で走ってしまうため、上手くQuizListの値が参照できません。
このような場合、どのような対処方法が良いのでしょうか。
該当のソースコード
ts
1import React, { ChangeEvent, useEffect, useState } from "react"; 2import { quizzes, Quiz } from "../data/quizzes"; 3 4export type GameSceneProps = { 5 setScene: (scene: string) => void; 6}; 7 8export const GameScene: React.FC<GameSceneProps> = ({ setScene }) => { 9 const [quizProgress, setQuizProgress] = useState<number>(1); 10 const [inputAnswer, setInputAnswer] = useState<string>(""); 11 const [inputCheck, setinputCheck] = useState<string>(""); 12 const [question, setQuestion] = useState<string>(""); 13 const [answer, setAnswer] = useState<string>(""); 14 const questinoNum = 3; 15 const [quizList, setQuizList] = useState<Quiz[]>([]); 16 17 const randomNumber = (max: number) => { 18 return Math.floor(Math.random() * max); 19 }; 20 21 const setQuizeez = () => { 22 const max = quizzes.length; 23 console.log(quizList); 24 for (let i = 0; i < questinoNum; i++) { 25 console.log(quizzes[randomNumber(max)]); 26 setQuizList([...quizList, quizzes[randomNumber(max)]]); 27 } 28 console.log(quizList); 29 }; 30 31 const onchangeText = (e: ChangeEvent<HTMLInputElement>) => { 32 setInputAnswer(e.target.value); 33 }; 34 35 const submitAnswer = () => { 36 console.log(inputAnswer); 37 console.log(answer); 38 if (inputAnswer == answer) { 39 setQuizProgress((count) => count + 1); 40 setinputCheck(""); 41 console.log(quizProgress); 42 console.log(quizList); 43 setQuestion(quizList[quizProgress - 1]["question"]); 44 setAnswer(quizList[quizProgress - 1]["answer"]); 45 } else { 46 setinputCheck("違うよ!"); 47 } 48 setInputAnswer(""); 49 }; 50 51 useEffect(() => { 52 console.log(quizList); 53 setQuizeez(); 54 setQuestion(quizList[quizProgress - 1]["question"]); 55 setAnswer(quizList[quizProgress - 1]["answer"]); 56 }, []); 57 58 useEffect(() => { 59 if (quizProgress === 4) { 60 setScene("result"); 61 } 62 }, [quizProgress]);
試したこと
以下のように、quizListが更新された際に、setQuestion()が動作するように変更した。
しかし、そもそもsetQuizeez()の中で、quizListが更新されない状態で、for文が回っているのでquestionNum=3の場合は3回目のquizzes[randomNumber(max)]がsetQuizListに収納された状態になってしまう。(本来はクイズが3問収納されて欲しい)
ts
1 const setQuizeez = () => { 2 const max = quizzes.length; 3 for (let i = 0; i < questinoNum; i++) { 4 console.log(quizzes[randomNumber(max)]); 5 setQuizList([...quizList, quizzes[randomNumber(max)]]); 6 } 7 }; 8 9 useEffect(() => { 10 console.log(quizList); 11 setQuizeez(); 12 }, []); 13 14 useEffect(() => { 15 if (quizList.length !== 0) { 16 setQuestion(quizList[quizProgress - 1]["question"]); 17 setAnswer(quizList[quizProgress - 1]["answer"]); 18 } 19 }, [quizList]); 20 21
追記①
以下のコードで実行したところ、setQuizeez()が2回実行され、配列に6つの要素が格納されてしまします。
setQuizeezは1度しか動かない認識ですが、なぜこのようなことが起こるのでしょうか?(開発環境のみuseeffectが2回動くようになっているみたいなのを見かけましたが、それが原因でしょうか?)
また、2回動くのはまだ分かるのですが、以下のコードにし、consoleを確認すると、ランダムで選出されたクイズ6つと、配列に格納されているクイズ6つが一致していないのですが、これはなぜでしょうか?
ts
1 const setQuizeez = () => { 2 const max = quizzes.length; 3 for (let i = 0; i < questinoNum; i++) { 4 var _q = quizzes[randomNumber(max)]; 5 console.log(_q); 6 setQuizList((quizList) => [...quizList, _q]); 7 // setQuizList((quizList) => [...quizList, quizzes[randomNumber(max)]]); 8 } 9 }; 10