Gilang Chandrasa Thoughts, stories, and ideas

Build Quiz App with ReactJS

The best way to learn is practice. So I create my own quiz app based on this article.

Let’s start

We’ll split our application into 2 components, a main component Quiz and stateless Question component.

Quiz Component

First we create our initial state.

export default class Quiz extends React.Component {

  constructor(props) {
      super(props)
      this.state = {
        quiz: {},
        index: 0,
        numberOfQuestions: 0,
        score: 0,
        answers: [],
        completed: false
      }
  }

}

For this example, we load quiz data from a json file and update the quiz state. We use the componentDidMount method.

I use jquery getJSON, but in real application you might want to use asynchronous fetch or some thing like that.

componentDidMount() {
  $.getJSON('./data/quiz.json', function(result) {
    this.setState({quiz: result})
    this.setState({'numberOfQuestions': result.questions.length})
  }.bind(this))
}

Here what the data in json file look like:

{
  "title": "Elementary Math Quiz",
  "questions": [
    {
      "question": "5 * 4 =",
      "answers": [
        {
          "point": 1,
          "label": "20"
        },
        {
          "point": 0,
          "label": "10"
        },
        {
          "point": 0,
          "label": "30"
        },
        {
          "point": 0,
          "label": "25"
        },
      ]
    }
    ...
  ]
}
render() {
  const {
    quiz, index, numberOfQuestions, score
  } = this.state
  return (
    <div>
      <h1>{quiz.title}</h1>
      {this.state.completed ?
        <div>
          <p>Congratulation, you finish the quiz</p>
          Your score is {score}
        </div>
      :
        <div>
        <h2>Question {index + 1} of {numberOfQuestions}</h2>
        {quiz.questions && index < quiz.questions.length ?
          <Question
            question={quiz.questions[index]}
            index={index}
            onAnswerSelected={(event) => this.handleAnswerSelected(event)}
            onSubmit={() => this.handleSubmit()}
          />
        : ''}
        </div>
      }
    </div>
  )
}

We have 2 more methods on Quiz component, handleSubmit will handle user clicking submit button and handleAnswerSelected will handle user choosing answer on question.

On handleSubmit we increment the index to show next question and at the end of quiz set completed as true and calculate the score.

handleSubmit() {
  if (this.state.index + 1 < this.state.numberOfQuestions) {
    this.setState({'index': this.state.index + 1})
  } else {
    this.setState({'completed': true})
    let score = this.state.score || 0
    this.state.answers.map((answer, i) => (
      score = score + this.state.quiz.questions[i].answers[answer].point
    ))
    this.setState({'score': score})
  }
}

We also need to handle user clicking an answer, update the answers state with this answer.

handleAnswerSelected(event) {
  let list = [...this.state.answers.slice(0, this.state.index),
              parseInt(event.target.value),
              ...this.state.answers.slice(this.state.index + 1)]
  this.setState({'answers': list})
}

Question Component

The stateless question component will render question based on props from the main component Quiz.

import React from 'react'

const Question = ({
  question,
  index,
  onAnswerSelected,
  onSubmit
}) => {
  return (
    <div>
      <h3>{question.question}</h3>
      <ol type="a">
      {question.answers.map((answer, i) =>
        <li key={`${index}-${i}`}>
          <input type="radio" name={`question_${index}`} id={`question_${index}_answer_${i}`} defaultChecked={false} value={i} onChange={onAnswerSelected} />
          {' '}
          <label htmlFor={`question_${index}_answer_${i}`}>{answer.label}</label>
        </li>
      )}
      </ol>
      <button onClick={onSubmit}>Submit</button>
    </div>
  )
}

export default Question

Here what the application looks like :

Quiz Obviously this is not perfect, you can cheat and lookup the JSON for the answers. A better solution would be send all the answers and calculate the result on the server.

Github You can get full source code here. https://github.com/gchandrasa/quiz-react