Is there a way to change an object's property from an array which is part of another object, nested in an array in React using only two components?

Advertisements

I am trying to change the isSelected property on a click event, but I am receiving "Cannot read properties of undefined". Also, the state after the event is undefined.

Here is the code:

App.js

function App() {
  const [quiz, setQuiz] = React.useState(false)
  const [quizData, setQuizData] = React.useState([])
  
  React.useEffect(() => {
    async function getQuizData() {
      const res = await fetch("https://opentdb.com/api.php?amount=5&type=multiple")
      const data = await res.json()
      setQuizData(formatData(data.results))
    }
    getQuizData()
  }, [])
  
  function formatData(questions) {
    let formatedData = questions.map(item => {
      return {
        id: nanoid(),
        question: item.question,
        correctAnswer: item.correct_answer,
        answers: shuffleAnswers([...item.incorrect_answers, item.correct_answer])
      }
    })
    return formatedData
  }

  function shuffleAnswers(answers) {
    let randomAnswers = [...answers].sort((a, b) => Math.random() - 0.5)
    let randomAnswerList = randomAnswers.map(item => {
      return {
        id: nanoid(5),
        isSelected: false,
        option: item
      }
    })
    return randomAnswerList
  }

  function holdAnswer(id) {
    setQuizData(prevQuizData => prevQuizData.map(item => {
      item.answers.map(element => {
        if (element.id === id) {
          return {...element, isSelected: !element.isSelected}
        } else {
          return element
        }
      })
    }))
  }

  function startQuiz() {
    setQuiz(prevQuiz => !prevQuiz)
  }

  return (
    <main>
        { 
          quiz 
        ? 
        <div className='quiz-container'>
            <Quiz 
              quizData={quizData}
              handleClick={holdAnswer}
            />
        </div> 
        :
          <Start 
            handleClick={startQuiz} 
          /> 
        }
    </main>
  )
}

Quiz.jsx

function Quiz (props) {  
    console.log(props.quizData)  
    let allAnswers = props.quizData.map(item => {

        return (
            <div className="question-container">
                <h3 key={item.id} id={item.id} className="question-title">{item.question}</h3>
                <div className="question-answers">
                    {item.answers.map(element => {

                        const styles = {
                            backgroundColor: element.isSelected ? "#D6DBF5" : "white"
                        }

                        return (
                            <button 
                                id={element.id}
                                key={element.id}
                                style={styles}
                                onClick={() => props.handleClick(element.id)}
                            >
                                {element.option}
                            </button>
                        )
                    })}
                </div>
            </div>
        )
    })

    return allAnswers
}

The formated data looks like this:

[
    {
        "id": "jB2iVcZ1qXqgdkvRS-dfH",
        "question": "When was the programming language &quot;C#&quot; released?",
        "correctAnswer": "2000",
        "answers": [
            {
                "id": "_RMiu",
                "isSelected": false,
                "option": "1998"
            },
            {
                "id": "MWrOT",
                "isSelected": false,
                "option": "1999"
            },
            {
                "id": "psITc",
                "isSelected": false,
                "option": "2001"
            },
            {
                "id": "I6Wp8",
                "isSelected": false,
                "option": "2000"
            }
        ]
    },
    {...and so on...}
]

I’ve tried in several ways to change the state with the setter function, however i am stuck and i can’t go further with the project.

Here is another approach that I’ve tried, but the results are the same:
https://github.com/ktrebor/quizzical/tree/main/src

>Solution :

You are not using the result of the item.answer.map(), so the state is not updated.

  function holdAnswer(id) {
    setQuizData(prevQuizData => prevQuizData.map(item => ({
      ...item,
      answers: item.answers.map(element => {
        if (element.id === id) {
          return {...element, isSelected: !element.isSelected}
        } 
        
        return element
      })
   })))
  }

Leave a ReplyCancel reply