Game of Thrones Quiz Game with React and GraphQL: Single Question Component
Publikováno: 17.6.2019
Create a folder called question
within the src/components
directory and a file index.js
within it. Open the file and copy the following into the file:
Create a folder called question
within the src/components
directory and a file index.js
within it. Open the file and copy the following into the file:
//src/components/question/index.js
import React, { useState } from "react";
const Question = ({ question, onNextClicked }) => {
const [answered, setAnswered] = useState(false);
const [selectedOption, setSelectedOption] = useState({});
// The event handler when an option is selected.
// TODO --- 1
// this function is used to check if the selected option matches the question's
// answer
// TODO --- 2
// After the question is answered, this function is called to reset the state of
// the component
// TODO --- 3
return (
<div className="question">
<div className="question-image-holder">
<img className="question-image" src={question.image.downloadUrl} alt={question} />
</div>
<section>
<div className="question-text-holder">
{answered && <button onClick={resetQuestion}>Next</button>}
<h4 className="question-text">{question.question}</h4>
</div>
{question.options.map((option, index) => {
return (
<button
key={option.id}
onClick={() => onOptionClicked(option)}
className={`question-option
// if the question is answered and the option is correct, add a
// "correct" class to the option
${answered && isCorrect(option) && "correct"}
// if the selected option matches the option, and the option is wrong,
// add a "wrong" class to the option
${selectedOption === option && !isCorrect(option) && "wrong"}`}
>
<span>
{answered ? (isCorrect(option) ? "✔" : "X") : (index + 1)}
</span>
{option}
</button>
);
})}
</section>
</div>
);
};
export default Question;
Again we declare state values using the useState
hook, the answered
variable is a boolean indicating when a question has been answered. The selectedOption
variable stores the option selected by the user. There are also some event handlers declared, the onOptionClicked
function is triggered on click of any option, and within the handler, we set the answered
state value to true
using the setAnswered
state transition function. We do the same for the selectedOption
value.
There’s also the isCorrect
function that compares a given option against the answer of the question and finally the resetQuestion
event handler is called when a user clicks the next button to navigate to the next question; within the handler we reset the state values to the default state and then call the onNextClicked
function passed as a prop from the parent function.
The next step is to add the event listeners for the elements we skipped. Go ahead and replace the following comments with their corresponding snippets:
TODO
--``- 1
:
const onOptionClicked = option => {
setAnswered(true);
setSelectedOption(option);
};
TODO
--``- 2
:
const isCorrect = option => {
return option === question.answer;
};
TODO
--``- 3
:
const resetQuestion = () => {
setAnswered(false);
setSelectedOption({});
onNextClicked(selectedOption);
};
Let’s apply some styles to the component. Create a file within the src/components/question
directory named question.css
and copy the following styles into it:
.question{
padding: 10px 14px;
display: flex;
}
.question-image-holder{
width: 55%;
}
.question-image{
max-width: 100%;
width: 100%;
border-radius: 15px;
margin-bottom: 10px;
object-fit: contain;
}
.question section{
width: 40%;
margin-left: 2%;
}
.question-text{
font-size: 17px;
font-weight: bold;
letter-spacing: 1px;
line-height: 1.7;
font-family: GOT;
color: rgba(0,0,0,0.6);
float: left;
}
.question-text-holder button{
color: white;
background: rgb(48, 48, 48);
padding: 12px 25px;
border: none;
font-weight: bold;
text-transform: uppercase;
border-radius: 8px;
float: right;
cursor: pointer;
margin-bottom: 10px;
}
// rest of the file can be found on github using the link below
NB: The length of the snippet has been reduced for the sake of readability. You can find the question.css stylesheet here on GitHub.
Add an import line for the file in the question component file. Add the following line at the end of the imports in the src/components/question/index.js
file:
import React, { useState } from "react";
import "./question.css";
// ...rest of the file
Next, we’ll render this component in the Questions
component. Open the questions/index.js
file and replace the // TODO -- create
a
component to render question here
with the Question
component. The snippet should be similar to the one below:
<Question
onNextClicked={onNextClicked}
question={currentQuestion}
key={currentQuestion.id}
/>
Testing the quiz app
After making these changes, you can run yarn start
or npm start
to view the latest changes on your browser.
At this point, we have a complete quiz application where users can answer questions and see there score at the end. We can take this further to show how users can create questions and even upload files with 8base.