Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Quiz Application in React (Using Redux)

4.90/5 (11 votes)
6 Jul 2019CPOL10 min read 31.2K   470  
A general purpose quiz application in React that can be used to run quizzes, mock tests, surveys, etc. This is an alternate version of my another article: Quiz Application in Angular.

Introduction

Quiz application in one form or the other is becoming a general requirement for most of the applications these days. Be it Survey, mock test, preparation, self evaluation, gathering information, actual objective test or exam. This quiz application will help you to get through your need with minimal or no modification.

This article presents you a simplified way to create your quiz application in React in just a few lines of code. The quiz application accepts the questions in json format. So, you can easily send the json from the server in the pre-defined JSON format and the React quiz application will render the quiz at the client side. The quiz also has review and display result section. If you wish to declare the result immediately, you can simply call another json with the answers in it and simply evaluate and display the results immediately. Alternatively, if you wish to just submit the answers to the server, you can also do so at ActionTypes.QuizSubmit method quiz.js reducer.

If you are looking for a quiz application in Angular 1.x, click here.

If you are looking for a quiz application in Angular, click here.

Image 1

Background

This article is the React version of Quiz Application in AngularJs. Since I got multiple email requests to have the quiz application for React as well, I thought of writing a separate article for the same.

This article assumes that you have basic knowledge of React, Redux, Bootstrap 4 and a bit of CSS. To run the code associated with this article, you should know a bit about how to do npm install and run it using react cli. Please visit React Cli if you wish to know how to run the application via react cli command.

Image 2

Using the Code

Using the code is simple. Just provide the json to the quiz application and it will render the quiz.

The json format should look like the following:

JavaScript
// javascript.json
{
    "id": 1,
    "name": "Javascript Quiz",
    "description": "Javascript Quiz (contains web development, Javascript, ES 5/6/7, etc.)",
    "questions": [
        {
            "id": 1010,
            "name": "Inside which HTML element do we put the JavaScript?",
            "questionTypeId": 1,
            "options": [
                {
                    "id": 1055,
                    "questionId": 1010,
                    "name": "javascript",
                    "isAnswer": false
                },
                {
                    "id": 1056,
                    "questionId": 1010,
                    "name": "scripting",
                    "isAnswer": false
                },

... [For full code, please see the sample attached.]

Note: You may not wish to include the IsAnswer property to the options as it might be unsafe for a secured quiz. In such case, you can simply send the questions with IsAnswer. Once the user submits the quiz, onSubmit method in quiz reducer, you can call the questions with answers and evaluate the answers.

About Quiz Application

The quiz application consists of mainly 4 components/views: Quiz, Questions, Review, and Result. If you wish to scale this application, you can very well consider other best practices followed for larger applications. For the sake of simplicity, I omitted some of the best practices. Quiz.js acts as a container to render questions/review/or result based on stage/mode of the quiz. Questions.js is used to render the quiz questions to UI. Review.js is used to render the status of running quiz and the Result.js renders the result once quiz is completed. Apart from this, App.css has been used to apply CSS style to the application.

The Quiz Running Logic

First of all, the App.js loads the quiz and questions by using load(quizId) method called at componentDidMount event. The quiz questions should be provided in a pre-defined json format as mentioned in javascript.json or other related json files present in data folder. Once the quiz and questions are loaded, user can answer the questions and the events are being captured by the same component. When user finally submits the quiz, you may submit the answers to the server in json format. Or load the questions with answers and evaluate the users answers to show the quiz result immediately. In the sample, I have provided the later approach for the sake of simplicity, but the first approach is also fairly simple.

The app.js file looks like the following:

JavaScript
// App.js
...
    const mapStateToProps = state => { return { ...state.quiz } };

    const mapDispatchToProps = dispatch => ({
      onQuizLoad: payload => dispatch({ type: ActionTypes.QuizLoad, payload }),
      onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
    });
...
    state = {
    quizes: [
      { id: 'data/javascript.json', name: 'Javascript' },
      { id: 'data/aspnet.json', name: 'Asp.Net' },
      { id: 'data/csharp.json', name: 'C Sharp' },
      { id: 'data/designPatterns.json', name: 'Design Patterns' }
    ],
    quizId: 'data/javascript.json'
    };
    
    pager = {
    index: 0,
    size: 1,
    count: 1
    }
    
    componentDidMount() {
    this.load(this.state.quizId);
    }
    
    load(quizId) {
    let url = quizId || this.props.quizId;
    fetch(`../${url}`).then(res => res.json()).then(res => {
      let quiz = res;
      quiz.questions.forEach(q => {
        q.options.forEach(o => o.selected = false);
      });
      quiz.config = Object.assign(this.props.quiz.config || {}, quiz.config);
      this.pager.count = quiz.questions.length / this.pager.size;
      this.props.onQuizLoad(quiz);
      this.props.onPagerUpdate(this.pager);
    });
    }
... 
[For full code, please see the sample attached.]

Quiz Configuration

The quiz configuration is an optional object that can be present in your <quiz>.json file. The config section allows you to customize your quiz the way you wish to do. The quiz application reads these configuration settings and applies these settings at the time of loading the quiz. The settings mainly consist of: shuffling the questions, showing/hiding pager, allowing back navigation, allowing auto move to next question. The details of this configuration are explained in "Quiz Features" section.

The quiz configuration looks like the following:

JavaScript
// csharp.json
...
    "config":{
        "shuffleQuestions": true,
        "showPager": false,
        "allowBack": true,
        "autoMove": true
    }
... 
[For full code, please see the sample attached.]

Services Used

For simplicity, API has been called by using fetch API of JavaScript in App.js. It is used to retrieve quiz json file. Fetch API is similar to XmlHttpRequest to make an Ajax call to the server and get the data asynchronously to client. For more details about Fetch API: click here.

Questions.js (HTML Display of Questions)

The Quiz View contains the UI for quiz questions, previous-next button, paging and related stuffs. I have chosen the structure of keeping one question per page but if you would like to show more than one question, you can simply change the pager.size value accordingly. For buttons and pagination UI, bootstrap has been used. The UI for questions and options has been set in styles.css.

The HTML for quiz view looks like the following:

HTML
// questions.js
   
<div id="quiz">
    <h2 className="text-center font-weight-normal">{this.props.quiz.name}</h2>
    <hr />
    {questions.map(q =>
        <div key={q.id}>
            <div className="badge badge-info">Question 
            {this.props.pager.index + 1} of {this.props.pager.count}.</div>
            <h3 className="font-weight-normal">{this.props.pager.index + 1}. 
            <span>{q.name}</span></h3>
            <div className="row text-left options">
                {
                    q.options.map(option =>
                        <div key={option.id} className="col-6">
                        <div className="option">
                        <label className="font-weight-normal" htmlFor={option.id}>
                        <input id={option.id} checked={option.selected} type="checkbox" 
                         onChange={() => this.onAnswer(q, option)} />
                        {option.name}
                        </label>
                        </div>
                        </div>
                    )
                }
            </div>
        </div>
    )}
    <hr />
    <div className="text-center">
    {this.props.quiz.config.allowBack && <button id="first" 
     className="btn btn-default" onClick={this.props.move}>First</button>}
    {this.props.quiz.config.allowBack && <button id="prev" 
     className="btn btn-default" onClick={this.props.move}>Prev</button>}
    <button id="next" className="btn btn-primary" onClick={this.props.move}>Next</button>
    <button id="last" className="btn btn-default" onClick={this.props.move}>Last</button>
    </div>
</div >

... [For full code, please see the sample attached.]

If you know a bit of React, this part is simple to understand. To display the questions and options, first, questions have been filtered based on pageSize and pageIndex, then.map has been used.

The review and result view has also been implemented similarly by iterating over the quiz questions and displaying the isAnswered flag. Below is the screenshot of Review and Result View.

Image 3

The quiz result view looks like the below image:

Image 4

The Styling

For styling and theme, bootstrap 4 has been used. For further styling based on the applications need, CSS classes has been added in App.css. I have tried my best to minimize the use of custom CSS, however, you may add as many custom CSS as you want. All the CSS styles are self-explanatory. If you wish to change the look and feel of the application, you can easily do so by changing the corresponding CSS class in App.css. I have made all attempts to make it responsive so that the application can be navigated easily on mobile phones and tablets too.

Quiz Features

This section acts as a small documentation kind of stuff for using the features of the quiz application. Please note that some of the features may not be working now, but I will make all the mentioned features functional eventually, as soon as I get the time for it. For other features, you may look at the Angular version of the application.

Shuffle Questions

shuffleQuestions property in config section is used to mention whether the questions should be randomized before showing or not. If true, the questions will be randomized. The default value is false.

Shuffle Options

shuffleOptions property in config section is used to mention whether the options can be randomized before showing or not. If true, the questions will be randomized. The default value is false. This feature is configurable from the quiz json file.

Show Pager

showPager property in config section indicates whether to show the pager element in quiz or not. If false, the numeric pager will not be shown. In this case, the user can still navigate via. first, prev, next and last button. The default value is true.

Auto Move

autoMove property in config section indicates whether to move to the next question automatically when the question is answered. If true, you don't need to press the Next button to move to the next question. The default value is false.

Load Quiz Dynamically

In mock test/practice exams, a user might want to change the quiz dynamically. To do so, you just need to call loadQuiz method and pass the URL of the new quiz. The attached sample or the demo link illustrates this. (In the sample app, you can load the quiz dynamically via changing the drop-down at the top right corner.)

HTML Questions

In many cases, you wish to have special characters, images, subscript, superscript, and other HTML elements. Adding HTML markup in question has been provided via angular sanitize. To add image or any HTML formatting in questions, just add the corresponding HTML tags in question.

I will keep on documenting other features as and when they will be available here.

Future Consideration

Due to time constraint, I could not implement other interesting features which might help you more if you wish to use this quiz application. I will add them later as and when I get time. I would also appreciate if someone can provide me the pull request in github, if they have implemented any of the pending features or existing features in a better way. :)

Some of the features I can think of are:

  • Quiz Options :In many cases, we need to randomize questions, randomize options, or set different marks for different questions. I am planning to provide these features which will be configurable from the quiz json file.
  • HTML Questions: In many cases, you wish to have special characters, subscript, superscript, and other html elements. So, in my opinion, providing html option in Quiz application would be a nice addition. This feature has been implemented.
  • Question Type: Currently, the application supports only multiple choice question. In many scenarios, it is needed to have multiple answer type question, true-false, etc. I will come up with this provision soon.
  • Timer : Timer is another important feature which every test/mock test wants to have. Timer/Stopwatch option shall also be provided as a configurable element in the quiz.
  • Image for Question: Image is also one of the most needed features in most of the quiz. I will come up with this feature in this article very soon. Image for questions can be provided by adding HTML questions.
  • Pick From Pool: In some scenarios, you might want to provide a pool of many questions as a json and want the Quiz application to randomly select n questions for the quiz. This feature will be implemented soon.
  • Optional Animation: Well, this is not an important feature but some people may want a bit of animation in his quiz application. I would, therefore, like to provide this feature as well.
  • Mobile Friendly: Even though this app is responsive and adjusts well with mobile and smaller screens, fonts and alignment needs to be adjusted according to the screen size. So, having a mobile friendly view is also a good and valid need. CSS has been optimized and css3 media query has been written to provide optimized view for mobile.
  • Tailored Questions: There are many cases when we require a tailored questions in a survey. This means that the next set of questions should come based on the option selected in current question of the survey. Based on email feedback from many users, I will give this feature a priority and will provide you as soon as I will get time for it.
  • Angular 2 version :Since Angular 2 is about to release, I would consider to re-write the application with better design and more flexibility in Angular 2.

Apart from the above features, if you feel some more features should be added, please let me know. I will try my best to implement them.

History

  • 2019-07-03: First version release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)