Как да създадете приложение за викторина с помощта на 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!