DEV Community

Lior Ben-David
Lior Ben-David

Posted on • Edited on

Creating a Trivia Game with React and Codesphere

There are few things as exciting as the “I know that one!” feeling one gets in a trivia game. The rush of being able to answer a niche and random question is a major reason why game shows have been a TV staple for almost as long as televisions have existed.

I recently came across OpenTDB, a free user-contributed trivia question database. Naturally, my first thought was to use it to create a trivia app. Though not incredibly complex, this project serves as a fun introduction to a number of React topics:

  • Developing and Deploying a React App with Codesphere

  • Working with an API

  • Using useEffect and useState

The final result:

Creating a React App with Codesphere

Codesphere is an online, collaborative programming environment and cloud provider. With Codesphere you can build your web app ready to scale without all the cumbersome config. In this project, Codesphere is going to allow us to create and deploy our React app seamlessly.

Codesphere is currently in early access but will launch fully soon. If you would like to join the early access, I have a number of keys that I can give out — leave me a comment with your email and I’ll send you one.

Once you are signed in, Codesphere should loan you in with an example project. We are going to delete the contents of the example project by running the following command in the terminal

rm -r ../app/*
Enter fullscreen mode Exit fullscreen mode

We can then go ahead and make our React App from the terminal with the command:

npx create-react-app trivia
Enter fullscreen mode Exit fullscreen mode

Finally, to make sure everything is running smoothly, we can run:

cd trivia && npm start
Enter fullscreen mode Exit fullscreen mode

Before we continue, it will be helpful to get rid of the create-react-app example and have our App.js look like the following:

Accessing the OpenTDB API

Open Trivia Database(OpenTDB) offers us an API that we can call to receive a given number of questions on a given topic. You can generate a custom API Url here:
Open Trivia DB
*To get started using the Open Trivia DB API, use this URL: For more settings or help using the API, read along below…*opentdb.com

In this project, we are going to be using the following API URL:

const url = "[https://opentdb.com/api.php?amount=10&category=19&type=multiple](https://opentdb.com/api.php?amount=10&category=18&type=multiple)"
Enter fullscreen mode Exit fullscreen mode

This URL will give us ten multiple-choice questions in the Math category, but I encourage you to pick whatever topic interests you!

Before we start, make sure to import useEffect and useState at the top of our App.js:

*import {useState, useEffect} from 'react';*
Enter fullscreen mode Exit fullscreen mode

Our next step is going to be defining a number of stateful variables with useState:

const [questions, setQuestions] = useState([])
const [loaded, setLoaded] = useState(false)
const [qInd, setQInd] = useState(0)
Enter fullscreen mode Exit fullscreen mode

The questions variable is going to be an array of question objects we get from the API. The loaded variable is going to let us know if the questions have been loaded yet, so we don’t accidentally try to access them too early. Finally, qInd(As in “question index”) is going to track the current question we are on in our array.

const loadQuestions = async () => {
let response = fetch(url).then(response => response.json()).then(data =>       {
   setQuestions(data.results)
   setLoaded(true)
  })
}
Enter fullscreen mode Exit fullscreen mode

To make sure that it’s working properly, let’s display the current question in our app. We have to make sure, however, that the question loads only if our loaded variable is true. The app will crash otherwise because you are attempting to load an element from an empty questions array.

return (
  <div className="App">
    {loaded && <div>
    <p className = "Question">{questions[qInd].question}</p>
    </div>
    }
  </div>
 );
Enter fullscreen mode Exit fullscreen mode

This should render the following:

Accepting User Inputs

If you look at the question object’s structure, the correct_answer value is stored separately from the rest of our answers. To get all our answers in the same place, we are going to insert the correct_answer randomly among our incorrect_answers whenever we load a question.

We’ll adjust our loadQuestions function like so:

const loadQuestions = async () => {
let response = fetch(url).then(response => response.json()).then(data =>       {
   insertCorr(data.results[0].incorrect_answers, data.results[0].correct_answer)
   setQuestions(data.results)
   setLoaded(true)
  })
}
Enter fullscreen mode Exit fullscreen mode

Where insertCorr() is defined as follows:

function insertCorr(arr, corr) {
    const randInd = Math.floor(Math.random() * 4)
    arr.splice(randInd, 0, corr)
}
Enter fullscreen mode Exit fullscreen mode

Now that all of our questions are in a singular array, we can map through them and create buttons fairly easily:

return (
  <div className="App">
    {loaded && <div>
    <p className = "Question">{questions[qInd].question}</p>
    * {questions[qInd].incorrect_answers.map((a) => {
      return <button key = {a} onClick = {(e) => handlePrsd(e, a)}>{a}</button>
     })}
    *</div>
    }
  </div>
 );
Enter fullscreen mode Exit fullscreen mode

For simplicity, we are going to set each buttons’ key to be its answer and have it call a handlePrsd function which we will define next. We are going to have this function accept two parameters: The click event and the answer that was pressed.

For our handlePrsd function, we want it to iterate to the next question. If we are at the end of our question array, we want it to load new questions:

*const handlePrsd = (e, ans) => {
   e.preventDefault()
   if(qInd + 1 < questions.length) {
      insertCorr(questions[qInd + 1].incorrect_answers, questions[qInd + 1].correct_answer)
      setQInd(qInd + 1)
    } else {
      loadQuestions()
      setQInd(0)
    }
}*
Enter fullscreen mode Exit fullscreen mode

Now we should see the following:

Managing Score

The next step is going to be to manage the user’s score. To do so, we are going to create some stateful variables:

const [score, setScore] = useState(0)*
*const [lastAns, setLastAns] = useState('black')
Enter fullscreen mode Exit fullscreen mode

The score variable will of course store the user’s score. The lastAns variable is going to store the color we want to display the score in, which will be black by default, green if the last answer was correct, and red if the last answer was incorrect.

We will then add a p tag in our App to display the score value in the correct color:

<p className = "Score" style=
Enter fullscreen mode Exit fullscreen mode


{{color: lastAns}}

Score: {score}

Finally, we need to update our handlePrsd function to change the score if correct. To do so, we will check if the pressed answer is the same as the correct answer. If so, we will add ten points, if not, we will deduct 10.

*const handlePrsd = (e, ans) => {
   e.preventDefault()

    if(ans == questions[qInd].correct_answer) {
       setScore(score + 10)
       setLastAns('green')
    } else {
       setScore(score - 10)
       setLastAns('red')
    }*

*    if(qInd + 1 < questions.length) {
      insertCorr(questions[qInd + 1].incorrect_answers, questions[qInd + 1].correct_answer)
      setQInd(qInd + 1)
    } else {
      loadQuestions()
      setQInd(0)
    }
}*
Enter fullscreen mode Exit fullscreen mode

And our final result is:

There we go! Functional, but quite ugly. Let’s style it!

Making Everything Look Pretty

.App {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  height: 100%;
  padding-top: 100px;
  text-align: center;
}

button {
  color: #fff;
  text-decoration: none;
  background: #1b95db;
  padding: 20px;
  border-radius: 10px;
  font-size: 22px;
  display: inline-block;
  border: none;
  transition: all 0.4s ease 0s;
  margin: 20px;
  border: 2px solid black;
}

button:hover {
  background: #3db1f5;
}

.Question {
  font-size: 32px;
  margin-right: 50px;
  margin-left: 50px;
}

.Score {
  font-size: 20px;
}
Enter fullscreen mode Exit fullscreen mode

Deploying with Codesphere

Deploying in Codesphere is as easy as running:

npm start
Enter fullscreen mode Exit fullscreen mode

in your terminal.

We can access the deployed web application like so:

Next Steps

This is obviously the bare minimum for a trivia game, but a lot of steps can be taken to make this better:

  • Buff the CSS and make it look great!

  • Allow users to change the category, difficulty, or type of questions. Edit the API URL based on their preferences!

  • Allow multiple players to play at once!

Top comments (1)

Collapse
 
eliasgroll profile image
Elias Groll

Awesome!