ReactJS – Step-by-Step Tutorial on Quiz Development

I just finished up writing a simplistic/trivial framework using ReactJS using which one could quickly create online quizzes. The code for this could be found on github on following page named as ReactJS-Quiz. I would like to take this opportunity to share AngularJS-Quiz  that I wrote sometime back. I must say that I found writing quiz framework using ReactJS more fulfilling as it got aligned to my OOP oriented thinking and I was not required to envisage templates etc as in case of AngularJS. That said, both frameworks are cool and have their own pluses and minuses. Please feel free to suggest if I missed on mentioning one or more key aspects. Also, sorry for typos.

I shall be presenting some of the following key concepts of ReactJS while explaining the functionality of Quiz framework.

  • How to Design UI by Components?
  • When to use Props vs State?
  • How to update parent state from child components?
  • How to use inline styles?
  • Sample code on using Form Elements

To make use of this framework, all that is required to be done is define JSON data model (or retrieve from server) as shown later in this article and that is it! In this blog, I will explain the design aspect of UI using ReactJS. Before that, let me quickly define this Quiz framework. Following are some of the features of first version of this quiz framework:

  • Set custom timing for each question paper
  • Marks based on complexity: One could associate marks appropriately to the questions based on their complexity. Take a look at JSON data model below.
  • Negative marking: One could apply negative marking to a question paper by assigning true or false
  • Displays result and passed/failed status

 

How to Design UI by Components?

As a starter, I would recommend you to quickly read the ReactJS page on thinking in terms of components. Briefly speaking, when designing UI, one has to do following:

  • Breakup the UI into distinct parts characterized by unique recognizable set of properties. These parts can also be called as components. One could adopt the Single Responsibility Principle to identify the components. As per SRP, a component should just do one thing. In other words, a component should have only one reason to change.See the picture below with components identified with red boundaries.
    Reactjs UI Design By Components

    Reactjs UI Design By Components

    When designing UI for tests/quiz, following are some of key components as shown above:

    1. Test: This is topmost component comprising of other components such as QuestionPaper and Scorecard. Test component represents property such as Name of the test, description of the test as this is responsibility of this component to display these information..
    2. QuestionPaper which represents a list of questions. The responsibility of this component is to display a list of Questions.
    3. Question representing the questions with multiple options as probable answers. The responsibility of this component is to display question and list of options.
    4. Scorecard representing UI to display the result and related text. The responsibility of this component is to display score and result text.

    The relationship between the components depicted below represents following:

    React is all about modular, composable components.

    • Test
      • QuestionPaper
        • Question
      • Scorecard
  • Defining JSON Data Model representing Components & Relationships: If you have identified the components correctly, you would find that these components and their relationship could be comfortably represented in form of JSON data model. Take a look at following JSON data model representing Test, QuestionPaper and Question as shown in above diagram:
    var test = {
    		name: "Sample Test",
    		description: "This is a sample test paper to demonstrate the ReactJS UI design by components.",
    		passCutoff: 0.33,		
    		applyNegativeMarking: false,
    		questions: [
    			{
    			id: "1",
    			qtext:"California is in which part of USA?",
    			options:[
    				{text:"East"},
    				{text:"Mid"},
    				{text:"West"},
    				{text:"South"}
    			],
    			ans:"West",
    			marks: 3
    		},
    		{
    			id: "2",
    			qtext:"Who is Prime Minister of India?",
    			options:[
    				{text:"Sonia Gandhi"},
    				{text:"Narendra Modi"},
    				{text:"Manmohan Singh"},
    				{text:"Rahul Gandhi"}
    			],
    			ans:"Narendra Modi",
    			marks: 2
    		},
    		{
    			id: "3",
    			qtext:"Which of the following is most popular Search Engine Company?",
    			options:[
    				{text:"Microsoft"},
    				{text:"Facebook"},
    				{text:"Google"},
    				{text:"Yahoo"}
    			],
    			ans:"Google",
    			marks: 1
    		},
    		]
    	};	
    

    In the above JSON data model, you would find “test” consisting of an array of questions which is modeled using “QuestionPaper” consisting of a list of “Question”s.

  • Identify Properties and State & Where does State Live: This is another key aspect of designing components. When selecting information as properties (props) or state, one should keep following in mind:
    • What should be props? Those piece of information which does not change with time, which can be computed using other information, or which are just a piece of information should be the candidate of “props”. In this example, following are some of the props:
      • Question and options text
      • Marks for each question
      • Percentage in Scorecard as this can be computed from totalscore
      • Result status in Scorecard as this can be computed based on percentage scored
    • What should be state? Those piece of information which changes with time, and can not be computed using other piece of information can be represented as “state”. In this example, following are some of the candidates for state:
      • Totalscore in the Scorecard as this would change based on the answers of each question
  • Code Samples for different Components
    • Test
      var Test = React.createClass({
      		getInitialState: function() {
      			return {totalscore : 0, testSubmitted: false};
      		},
      		handleChange: function(result) {
      			this.setState({totalscore: result.totalscore, testSubmitted: true});
      		},
      		render: function(){						
      			var totalmarks = 0;
      			this.props.details.questions.map(function(question){
      				totalmarks += question.marks;
      			});
      			return(
      				<div>					
      					<h1>{this.props.details.name}</h1>
      					<hr className="divider"/>
      					<div>{this.props.details.description}</div>
      					<table className="table">
      						<tr>
      							<td className="col-md-9">
      							<QuestionPaper questions={this.props.details.questions} applyNegativeMarking={this.props.details.applyNegativeMarking}
      							 onSubmitted={this.handleChange}/>
      							 </td>
      							 <td className="col-md-3">
      							<Scorecard score={this.state.totalscore} testSubmitted={this.state.testSubmitted} percentage={Math.round(this.state.totalscore*100/totalmarks)}/>					
      							</td>
      						</tr>
      					</table>
      				</div>
      			);
      		}
      	});		
      
    • QuestionPaper
      var QuestionPaper = React.createClass({
      		getInitialState: function() {
      			return {totalscore : 0};
      		},
      		handleChange: function(score) {
      			this.setState({totalscore: this.state.totalscore + score});
      		},
      		handleSubmitted: function(event) {
      			var result = {totalscore: this.state.totalscore};
      			this.props.onSubmitted( result );			
      		},
      		render: function(){
      			var questionAnswers = this.props.questions.map(function(question){
      				return(
      					<tr><td><Question question={question.qtext} number={question.no} options={question.options} answer={question.ans} marks={question.marks} applyNegativeMarking={this.props.applyNegativeMarking} onAnswered={this.handleChange}/></td></tr>
      					);
      			}, this);
      			return(
      				<div>					
      					<table className="table table-striped">{questionAnswers}</table>
      					<div><input type="button" className="btn btn-primary" value="Submit" onClick={this.handleSubmitted}/></div>
      				</div>
      				
      			);
      		}
      	});		
      
    • Question
      var Question = React.createClass({
      		getInitialState: function() {
      			return {
      				correctAnswerRecorded: false,
      				negativeAnswerRecorded: false				
      			};
      		},
      		handleChange: function(event) {
      			var score = 0;
      			if( event.target.value == this.props.answer) {				
      				if( this.state.correctAnswerRecorded === false ) {					
      					if( this.props.applyNegativeMarking === true && this.state.negativeAnswerRecorded === true ) {
      						score = 1 + this.props.marks;
      					} else {
      						score = this.props.marks;
      					}
      				}				
      				this.state.correctAnswerRecorded = true;
      				this.state.negativeAnswerRecorded = false;
      			} else {				
      				if( this.props.applyNegativeMarking === true && this.state.negativeAnswerRecorded === false ) {
      					if( this.state.correctAnswerRecorded === true ) {
      						score = -1 - this.props.marks;
      					} else {
      						score = -1;	
      					}
      					
      				} else {
      					if( this.state.correctAnswerRecorded === true ) {
      						score = -this.props.marks;
      					} 
      				}
      				this.state.negativeAnswerRecorded = true;
      				this.state.correctAnswerRecorded = false;
      			}
      			this.props.onAnswered(score);
      		},
      		render: function(){
      			var qname = "option" + this.props.number;
      			var qoptions = this.props.options.map(function(option){
      			return (
      				<div><input type="radio" name={qname} value={option.text} onChange={this.handleChange}/> {option.text}</div>
      				);
      			}, this);
      			return(
      				<div>
      					<div><strong>Q</strong>: {this.props.question}</div>
      					<div>{qoptions}</div>
      					<br/>
      				</div>
      			);
      		}
      	});		
      
    • Scorecard
      var Scorecard = React.createClass({		
      		render: function(){
      			var status = "Test not submitted!";
      			if( this.props.testSubmitted == true ) {
      				if( this.props.percentage < 33 ) {
      					status = "Sorry, you could not pass the test. Try again later!"
      				} else {
      					status = "Congratulations!! You passed the test.";
      				}				
      			}
      			return(
      				<div className="list-group">
      					<div className="list-group-item active">Test Result</div>
      					<div className="list-group-item">Score: <strong>{this.props.score}</strong></div>
      					<div className="list-group-item">Percentage: <strong>{this.props.percentage} %</strong></div>
      					<div className="list-group-item">Status: <strong>{status}</strong></div>
      				</div>
      			);
      		}
      	});
      	
      

Please feel free to share any comments/suggestions that you may have. I would extend this framework with other features soon.

Ajitesh Kumar

Ajitesh Kumar

I have been recently working in the area of Data analytics including Data Science and Machine Learning / Deep Learning. I am also passionate about different technologies including programming languages such as Java/JEE, Javascript, Python, R, Julia, etc, and technologies such as Blockchain, mobile computing, cloud-native technologies, application security, cloud computing platforms, big data, etc. I would love to connect with you on Linkedin. Check out my latest book titled as First Principles Thinking: Building winning products using first principles thinking.
Posted in Javascript, Web. Tagged with , .