I'm new to React and am trying to create this trivia app from an API.
I'm currently trying to store the answers selected by the user so that I can then compare them with the correct answers.
I'm using radio inputs for the possible choices for the answers, and my idea is to update my state once the radio input is checked. I am able to update the state (at least it looks like it on the console), but the button won't check and in this case, I can't style it.
For context: I'm pulling 5 multiple choice questions from the API and I have 3 components: <App />
, <Quiz />
, and <Question />
.
Here's the <App />
content:
import { useState, useEffect } from 'react'
import './App.css'
import Quiz from "./components/Quiz"
function App() {
const [quiz, setQuiz] = useState(false)
const [data, setData] = useState([])
const [score, setScore] = useState(0)
const [countCorrect, setCountCorrect] = useState(0)
function startQuiz() {
setQuiz(true)
}
useEffect(() => { // fetch data from API and save it on the data state
fetch("https://opentdb.com/api.php?amount=5&difficulty=easy&type=multiple")
.then(response => response.json())
.then(data => setData(Object.values(data)[1]))
}, [])
return (
<main className="container">
{!quiz ?
(<section className="starter--container">
<h1>Quizzical</h1>
<button onClick={startQuiz}>Start quiz</button>
</section>) :
<Quiz data={data}/>
}
</main>
)
}
export default App
And here's the <Question />
, which is where the problem is
import { useState } from "react";
import { nanoid } from "nanoid";
export default function Question({ data }) {
const [userSelections, setUserSelections] = useState([{
question1: "",
question2: "",
question3: "",
question4: "",
question5: "",
}]);
function handleQuestion(event) {
const { name, value } = event.target
setUserSelections(prevState => {
return {
...prevState,
[name]: value,
}
})
}
return (
<div className="quiz--container">
{data.map(({ question }, index) => {
data[index].id = index;
const currentIndex = data[index].id;
const currentAnswers = data.find((question) => question.id === currentIndex);
const allChoices = [...currentAnswers.incorrect_answers, currentAnswers.correct_answer].sort(() => Math.random() - 0, 5);
return (
<div key={index} className="question--container">
<p className="question">
{question.replace(/"/g, '"').replace(/'/g, "'")}
</p>
<form className="choices--container" key={nanoid()}>
{allChoices.map((choice, index) => {
const questionNumber = `question${currentIndex 1}`
const optionNumber = `Option${index 1}`
return (
<div className="choice" key={index}>
<input
type="radio"
id={`${questionNumber}${optionNumber}`}
name={questionNumber}
value={choice}
onChange={handleQuestion}
/>
<label className={`choice--label`} htmlFor={`${questionNumber}${optionNumber}`}>
{choice}
</label>
</div>
)
})}
</form>
</div>
);
})}
</div>
);
}
I tried to include checked={userSelections.questionNumber === choice}
to the input element, but then I noticed the state is updating in a weird way: it's creating a new nested object instead of updating the original, like in the screenshot below:
CodePudding user response:
Just add checked props and check conditionally selected answer.
<input
type="radio"
id={`${questionNumber}${optionNumber}`}
name={questionNumber}
value={choice}
selected={true}
onChange={handleQuestion}
checked={choice === userSelections[questionNumber]}
/>;