Game of Thrones Quiz Game with React and GraphQL

Single Question Component

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.

Like this article? Follow @codebeast on Twitter