I am developing a simple multiple choice question and answer app using react component functions.
Each question has 2 or 4 possible answers where I have the data configured in nested objects / arrays an example is like below:
{
question: 'what country is....',
id: nanoid(),
answers:
[{
answer: 'Egypt',
id: nanoid(),
isCorrect: true,
isChecked: false
},
{
answer: 'Greece',
id: nanoid(),
isCorrect: false,
isChecked: false
},
{......}
my problem is when I want to flip the 'isChecked' attribute (when someone clicks on the corresponding button) I need to iterate through the object and then iterate through the 'answers' array, however I am unable to set state correctly, I tried nested map methods, I also am trying nested for loops, but I just can't get the desired results no matter what I try.
What is a simple method when having nested objects and arrays to setState correctly and reliably?
here is my code below
App.js
import React from 'react'
import Questions from './Questions'
import { nanoid } from 'nanoid'
import { decode } from 'he'
export default function App() {
const [tempState, setTempState] = React.useState(true)
const [data, setData] = React.useState([])
React.useEffect(() => {
fetch("https://opentdb.com/api.php?amount=5&category=9&difficulty=medium")
.then(res => res.json())
.then(info => stateFormat(info.results))
}, [])
function stateFormat(rawData) {
// console.log(rawData)
let correctAnswers = [];
for (let i = 0; i < rawData.length; i++) {
correctAnswers.push({
question: rawData[i].question,
id: nanoid(),
answers: [{
answer: rawData[i].correct_answer,
id: nanoid(),
isCorrect: true,
isChecked: false
}]
})
if (rawData[i].incorrect_answers.length > 2) {
for (let j = 0; j < 3; j++) {
correctAnswers[i].answers.push({
answer: rawData[i].incorrect_answers[j],
id: nanoid(),
isCorrect: false,
isChecked: false
})
}
} else {
correctAnswers[i].answers.push({
answer: rawData[i].incorrect_answers[0],
id: nanoid(),
isCorrect: false,
isChecked: false
})
}
}
setData(correctAnswers)
console.log(data)
}
function handleChange(id) {
setData(prev => {
for(let i = 0; i < prev.length; i++) {
for(let j = 0; j < prev[i].answers.length; j++) {
// console.log(prev[i].answers[j])
if(prev[i].answers[j].id === id) {
console.log({...prev[i].answers[j], isChecked: !prev[i].answers[j].isChecked})
return {...prev[i].answers[j], isChecked: !prev[i].answers[j].isChecked}
} else {
// return {...prev[i].answers[j]}
}
}
}
})
}
// function handleChange(id) {
// setData(prev => prev.map(item => {
// return (item.answers.map(button => {
// return button.id === id ?
// { ...button, isChecked: !button.isChecked } :
// button
// }))
// }))
// }
const questionElements = data.map(item => (
<Questions
key={item.id}
question={item.question}
answers={item.answers}
handleChange={handleChange}
/>
))
return (
<main>
<img className="blob--top"
src={require('./blobs.png')}
/>
<img className="blob--bottom"
src={require('./blobs1.png')}
/>
{tempState ?
<div className="quiz--container">
<div>
{questionElements}
<button className="game--check button">Check answers</button>
</div>
</div> :
<>
<div className="title--container">
<div className="title--init">
<h2 className="title--header">Quizzical</h2>
<h4 className="title--subheader">A battle of the brains</h4>
<button className="game--start button"
onClick={() => setTempState(!tempState)}
>
Start quiz</button>
</div>
</div>
</>
}
</main>
);
}
Questions.js
import React from 'react'
import { decode } from 'he'
export default function Questions(props) {
// console.log(props)
const answerButton = props.answers.map(item => (
<button
isCorrect={item.isCorrect}
isChecked={item.isChecked}
id={item.id}
style={{backgroundColor: item.isChecked ? "#D6DBF5" : ""}}
onClick={() => props.handleChange(item.id)}>{decode(item.answer)}
</button>
))
return (
<div className="question--container">
<h4>{decode(props.question)}</h4>
<div className="question--items">
{answerButton}
</div>
<img src={require('./Line13.png')} />
</div>
)
}
the issue is with my handleChange(id) function, what can be done to return the appropriate object?