React - display answer as a list

Viewed 394

I am creating a react quiz app but I'm trying to figure out how to get my answer array to show as a list. This is my code so far

Load data succesfully loads the data from an api, when i do a console log on my answers variable I can see an array for each question with four answers which is correct.

const loadData = async () => {
  let response = await fetch(
    "https://opentdb.com/api.php?amount=10&category=22&difficulty=medium&type=multiple"
  );
  const data = await response?.json();
  console.log(data);

  const getQuestions = data.results.map((item) => {
    const question = item.question;

     const answers = [...item.incorrect_answers, item.correct_answer];
   
//console.log(answers) shows this as an example 
 ["Quebec", "Ontario", "Nova Scotia", "Alberta"]

    return {
      question: question,
      answers: answers,
    };
  });
  return getQuestions;
};



function App() {
//create useState hook to pass data into
const [showData, setData] = useState([]);

//pass the data into a useeffect hook and setData to the loadData method from above
useEffect(() => {
    (async () => {
      const getData = await loadData();
      setData(getData);
      
    })();
  }, []);


  return (
    <React.Fragment>
      {showData.map((data) => (
        <>
          <div>
            <h1>{data.question}</h1>
          
          <ul>{data.answers}</ul>
         </div>
        </>
      ))}
 
    </React.Fragment>
  );
}

The return function outputs the question but if i try to display the answers as a list so I can get each one seperately, it just puts them on the same line.

e.g: Which city is the capital of Switzerland? Zürich,Frankfurt,Wien,Bern

Any help would be appreciated :)

4 Answers

You can iterate though your answers array with another map:

<ul>
  {
    data.answers.map((answer) =>
       // It's a good practice to apply a unique identifier as key to your list
       // Not the index, however, as it may change 
       <li key={someValue}>answer</li> 
    ) 
  }
</ul>

You should iterate the answers array instead of rendering them directly. Also ul is the parent tag for unordered list, you should use li for listing the children.

import { useEffect, useState } from "react";

export default function App() {
  const [showData, setData] = useState([]);
  const loadData = async () => {
    let response = await fetch(
      "https://opentdb.com/api.php?amount=10&category=22&difficulty=medium&type=multiple"
    );
    const data = await response?.json();
    console.log(data);

    const getQuestions = data.results.map((item) => {
      const question = item.question;
      const answers = [...item.incorrect_answers, item.correct_answer];
      return {
        question: question,
        answers: answers
      };
    });
    return getQuestions;
  };

  useEffect(() => {
    (async () => {
      const getData = await loadData();
      setData(getData);
    })();
  }, []);

  return (
    <>
      {showData.map((data,i) => (
        <>
          <div key={i}>
            <h4>{data.question}</h4>
            // map the answers separately
            {data.answers.map((item,j)=><li key={j}>{item}</li>)}
          </div>
        </>
      ))}
    </>
  );
}

Check code here : https://codesandbox.io/s/floral-leftpad-zcwew?file=/src/App.js:0-1063

Try this out ‍♂️

function App() {
  //create useState hook to pass data into
  const [showData, setData] = useState([]);

  //pass the data into a useeffect hook and setData to the loadData method from above
  useEffect(() => {
    (async () => {
      const getData = await loadData();
      setData(getData);
    })();
  }, []);

  return (
    <React.Fragment>
      {showData.map((data, index) => {
        return (
          <div key={index}>
            <div>
              <h1>{data.question}</h1>
              <ul>
              {data.answers.map((answer, index) => {
                return (
                  <li key={index}>{answer}</li>
                )
              })}
              </ul>
            </div>
          </div>
        );
      })}
    </React.Fragment>
  );
}

export default App;

There are multiple issues here:

  1. <ul>{data.answers}</ul> won't work because data.answers is an array. You'll need to map the elements of this array into <li> elements just as you did in the parent.
  2. Whenever you use map to create a list, you must add a unique key on each element, typically an id. I assume questions and answers are unique, so you can use the data itself as the key.
  3. Your code doesn't check response.ok before accessing response.json(). Yes, you do use a question mark operator here so that specific operation is safe, but then the undefined.results on the next line crashes, so it just pushes the buck and obfuscates the fetch error further.
  4. Remove the superfluous fragment <>.
  5. In es6, {questions: questions} can just be {questions}.
  6. Don't forget to shuffle your answers when you continue working on the app.

<script type="text/babel" defer>
const {Fragment, useEffect, useState} = React;

const loadData = async (
  url="https://opentdb.com/api.php?amount=10&category=22&difficulty=medium&type=multiple"
) => {
  const response = await fetch(url);
  
  if (!response.ok) {
    throw Error(response.status);
  }
  
  return (await response.json()).results.map(
    ({question, incorrect_answers, correct_answer}) => ({
      question, 
      answers: [...incorrect_answers, correct_answer]
    })
  );
};

const App = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    loadData()
      .then(setData)
      .catch(err => console.error(err))
    ;
  }, []);

  return (
    <Fragment>
      {data.map(({question, answers}) => (
        <div key={question}>
          <h3>{question}</h3>
          <ul>
            {answers.map(a => <li key={a}>{a}</li>)}
          </ul>
        </div>
      ))}
    </Fragment>
  );
}

ReactDOM.render(<App />, document.body);

</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

Related