Як створити вікторину за допомогою React - за допомогою порад та початкового коду

У цьому посібнику для початківців React ми збираємося створити вікторину. Ми будемо працювати зі складними об'єктами стану, як обробляти різні зачепи стану та відображати речі на основі стану.

Перевір:

Спробуйте самі

Якщо ви хочете спробувати спочатку самостійно, ось сценарії (ви також можете взяти початковий код нижче):

  • Коли користувач натискає кнопку, має з’явитися наступне запитання
  • Якщо користувач отримує правильне запитання, він повинен збільшити свій бал
  • Коли користувач дійде до кінця вікторини, повинен бути показаний їх загальний бал

Покрокове відео

Кодекс стартера

Візьміть його на GitHub тут.

Ходімо!

Якщо ви відкриєте початковий код і перейдете до App.js , ви побачите, що я дав вам список питань / відповідей, що зберігаються як масив, який називається питаннями . Це наша вікторина.

Наша перша мета - взяти дані запиту з масиву та відобразити їх на екрані.

Ми вилучимо твердокодований текст і взятимемо дані з першого запитання поки що, аби все почалося. Пізніше ми будемо турбуватися про зміну запитань.

У нашому JSX видаліть закодований текст запитання та введіть, {questions[0]}щоб отримати перший елемент (або запитання) у нашому масиві запитань.

 {questions[0]} 

Надання запитань та відповідей

Перше запитання - це об’єкт, тому ми можемо використовувати “крапкові позначення”, щоб отримати доступ до властивостей. Тепер ми просто зробимо, {question[0].questionText}щоб отримати доступ до тексту запитання для цього об’єкта:

 {questions[0].questionText} 

Збережіть і запустіть програму. Зверніть увагу, як оновлюється текст. Пам’ятайте, що ми просто беремо текст першого запитання з першого об’єкта у нашому масиві запитань.

Ми застосуємо подібний підхід до варіантів відповідей. Видаліть закодовані кнопки, і ми використовуватимемо функцію map, щоб переглядати варіанти відповіді на дане питання.

Запам’ятайте цикл функції map над масивом і дасть нам поточний елемент, у якому зараз знаходиться цикл, у вигляді змінної.

Замініть div "розділу відповідей" таким:

 {questions[0].answerOptions.map((answerOption, index) => ( {answerOption.answerText} ))} 

Збережіть і запустіть програму. Зверніть увагу, як з’являються чотири кнопки відповіді, а текст відображається динамічно.

Підсумуємо:

  • Ми отримуємо перше питання з масиву питань: questions[0]
  • Перше питання - це об’єкт, який містить масив answerOptions. Ми можемо отримати цей масив, використовуючи крапкові позначення:questions[0].answerOptions
  • Оскільки answerOptionsмасив є масивом, ми можемо відобразити це:questions[0].answerOptions.map
  • Усередині функції карти ми відображаємо по кнопці для кожного answerOptionі відображаємо текст

Зміна питань за допомогою стану

А тепер повернімось до нашого JSX. Зверніть увагу, як якщо ми перейдемо questions[0]на questions[1], або questions[2], інтерфейс користувача оновиться. Це пов’язано з тим, що він бере дані з різних питань у нашому масиві запитань, залежно від індексу.

Що ми хочемо зробити, це скористатися об’єктом стану, щоб зафіксувати питання, на яке зараз перебуває користувач, та оновити це, коли натискається кнопка відповіді. Це видно з запуску коду в останньому прикладі.

Далі додайте об’єкт стану, який буде містити поточний номер запитання, на якому користувач. Це буде ініціалізовано до 0, тому вікторина приймає перше питання з масиву:

const [currentQuestion, setCurrentQuestion] = useState(0); 

Тепер ми хочемо замінити жорстко закодований "0" у нашому JSX цією змінною. Спочатку для тексту запитання:

 {questions[currentQuestion].questionText} 

А також для розділу питань:

 {questions[currentQuestion].answerOptions.map((answerOption, index) => ( {answerOption.answerText} ))} 

Тепер, якщо ініціалізувати currentQuestion до чогось іншого, ніж 0, наприклад 1 або 2, інтерфейс користувача оновиться, щоб показати питання та відповіді на це конкретне питання. Дуже здорово!

Давайте додамо трохи коду, щоб, натискаючи відповідь, ми збільшували значення currentQuestion, щоб перейти до наступного питання.

Створіть нову функцію з назвою handleAnswerButtonClick . Це те, що буде викликано, коли користувач натискає відповідь.

Ми збільшимо поточне значення запитання на одиницю, збережемо його у нову змінну та встановимо цю нову змінну у стан:

const handleAnswerButtonClick = (answerOption) => { const nextQuestion = currentQuestion + 1; setCurrentQuestion(nextQuestion); }; 

Потім додайте подію onClick до нашої кнопки так:

 handleAnswerButtonClick()}>{answerOption.answerText} 

If we try this, you'll see it works, until we get to the end:

So what’s happening? Well in our handleAnswerButtonClick function, we’re incrementing the number and setting it to state. Thats OK.

But remember that we use this number to access an array, in order to get the question and answer options. Once we get to 5, it will break as there is no 5th element!

Let’s do a check to make sure we don’t go over the limit. In our handleAnswerButtonClick function let’s add the following condition:

if (nextQuestion < questions.length) { setCurrentQuestion(nextQuestion); } else { alert('you reached the end of the quiz'); } 

This basically says if the next question number is less than the total number of questions, update the state to the next question. Else, we’ve reached the end of the quiz, so show an alert for now.

Showing the score screen

Instead of showing an alert, what we want to do is show the “score” screen.

If we look at the JSX, you’ll notice that I’ve put the markup in here for you, we just need to replace “false” with the logic.

So how do we go about this? Well this is a perfect thing to put in state!

Add another state object which will store wether we want to show the score screen or not:

const [showScore, setShowScore] = useState(false); 

And replace false with showScore in our JSX:

 {showScore ? // ... score section markup : // ... quiz question/answer markup} 

Nothing will change, but if we change the state value to true, then the score div will show. This is because everything is wrapped in a ternary, meaning:

“If showScore is true, render the score section markup, else, render the quiz question/answer markup”

Now, we want to update this state variable when the user has reached the end of the quiz. We have already written the logic for this in our handleAnswerButtonClick function.

All we have to do is replace the alert logic that updates the showScore variable to being true:

if (nextQuestion < questions.length) { setCurrentQuestion(nextQuestion); } else { setShowScore(true); } 

If we click through the answers of the quiz, it’ll show the score section when we get to the end. At the moment, the text and score shown is a hardcoded string, so we should make it dynamic.

Saving the score

Our next task is to hold a score somewhere in our app, and increment this value if the user selects the correct option.

The logical place to do this is within the “handleAnswerOptonClick” function.

Remember when we iterate over the answerOptions, the map function gives us an object for each which includes the questionText, and a boolean value showing whether that answer is correct or not. This boolean is what we will use to help us increment our score.

In our button, update the function like so:

onClick={()=> handleAnswerButtonClick(answerOption.isCorrect) 

Next update the function to accept this parameter:

const handleAnswerButtonClick = (isCorrect) => { //... other code }; 

Now we can add some logic here in our function. For now we want to say “if isCorrect is true, we want to show an alert”:

const handleAnswerButtonClick = (isCorrect) => { if (isCorrect) { alert(“the answer is correct!”) } //...other code }; 

This is the same as if(isCorrect === true), just a shorthand version. Now if we try this you will see we get an alert when we click on the correct answer.

Just to recap so far:

  • When we iterate over the buttons, we pass the isCorrect boolean value for that button to the handleAnswerButtonClick function
  • In the function we check if this value is true and show an alert if it is.

Next we want to actually save the score. How do you think we do this? If you said state value you are correct!

Go ahead and add another state value called “score”. Remember to prefix the function to change the value with “set” so it’ll be setScore. Initialise it to 0:

const [score, setScore] = useState(0); 

Next instead of showing an alert, we want to update our score by 1 if the user got the answer correct.

In our handleAnswerButtonClick function, remove the alert and increment our score by one:

const handleAnswerButtonClick = (isCorrect) => { if (answerOption.isCorrect) { setScore(score + 1); } //...other code }; 

Showing the score

To show the score we just have to make a small change to our rendering code. In our JSX, remove the hardcoded string in the score section, and add this new variable:

 You scored {score} out of {questions.length} 
 You scored {score} out of {questions.length} 

Now if we run through the answers, the score is dynamic and will display correctly at the end!

One last thing before we wrap up our quiz app: you’ll notice the current question shown on the UI is always “1”, since it's hardcoded. We need to change this to be more dynamic.

Replace the "question-count" with the following:

 Question {currentQuestionIndex + 1}/{questions.length} 

Remember we need the +1 as computers start counting from 0 and not 1.

Want more project ideas?

Why not try building some React projects to boost your learning even further? Every week I send out a new project for you to try a working example, starter code, and tips. Subscribe to get this straight to your inbox!