So I've been stuck on this for a week now even though it's probably easy... I'm trying to make an array of objects using map over a hook. The final objective is to create a new hook from this new array and to be able to interact with it with some callbacks. The issue is that when I try to just render it (before creating a hook from it), I have this error showing up :
Uncaught TypeError: Cannot read properties of undefined (reading 'value')
So here is my code :
import { useState } from 'react'
import { useEffect } from 'react'
import { nanoid } from 'nanoid'
import './App.css'
import FirstPage from './components/FirstPage'
import Quiz from './components/Quiz'
function App() {
const [startGame, setStartGame] = useState(false)
const [quizData, setQuizData] = useState([])
useEffect(()=>{
fetch(`https://opentdb.com/api.php?amount=5`)
.then (res => res.json())
.then (data => setQuizData(data.results))
}, [])
function gameStart(){
setStartGame(true)
}
const displayQuestions = quizData.map(({question, correct_answer, incorrect_answers})=>{
const answer=[]
const answers=[]
const questions ={
value : question,
id : nanoid()
}
const answer1={
text : incorrect_answers.slice(0,1),
is_selected : false,
is_correct : false,
id : nanoid()}
const answer2={
text : incorrect_answers.slice(1,2),
is_selected : false,
is_correct : false,
id : nanoid()}
const answer3={
text : correct_answer,
is_selected : false,
is_correct : true,
id : nanoid()}
const answer4={
text : incorrect_answers.slice(2),
is_selected : false,
is_correct : false,
id : nanoid()}
const answersOptions = answers.push(answer1,answer2,answer3,answer4)
const quiz = answer.push(questions, answers)
return <div>
{
answer.map(({questions, answers}) => (
<ul key={questions.id} >
<li className='questions'>{questions.value}</li>
<button className='answers'>{answers.text}</button>
</ul>
))
}
</div>
})
return (
<>
<h1 className='bloc'></h1>
<h1 className='bloc2'></h1>
{startGame
?
<Quiz displayQuestions={displayQuestions}/>
:
<FirstPage gameStart={gameStart}/>
}
</>
);
}
export default App
The main project is to make a quiz using some datas from an API. Thank you for your help !
CodePudding user response:
First of all, you can't just throw everything in the answer array and destruct it that way. Since you're getting the elements one by one from the quizData array anyway, you're only interested in a single question here, not questions. You must write the question on one line in the jsx you return and return the answers in the loop, otherwise you will have rewritten the same question for each answer.
const displayQuestions = quizData.map(
({ question, correct_answer, incorrect_answers }) => {
const answers = [
{
text: correct_answer,
is_selected: false,
is_correct: true,
id: nanoid()
},
...incorrect_answers.map((inc) => ({
text: inc,
is_selected: false,
is_correct: false,
id: nanoid()
}))
];
const q = {
value: question,
id: nanoid()
};
// to shuffle
let shuffledAnswers = answers
.map((value) => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value);
return (
<div>
<div key={q.id}>{q.value}</div> {/* question part */}
{shuffledAnswers.map(({ id, text, is_selected, is_correct }) => (
<ul key={id}>
<li className="answers">
{text}
</li>
</ul>
))}
</div>
);
}
);
CodePudding user response:
The issue is that you are trying to push
the questions
and answers
variables as items to the answer
array, but in fact you need to create an object
with the questions
and answers
properties and push
to the array after.
Doing the way you did, the array would be equal to [questions, answers]
, but in fact you want it to be [{questions, answers}]
, then you could destruct the item and get the questions
and answers
properties.
So, in the line:
const quiz = answer.push(questions, answers);
It should be:
const quiz = answer.push({questions, answers});
But, the answer
array is not necessary in this situation, since it's going to have just 1 item anyway.