November 20, 2015 Benjamin Bonny 4 min read

This is a quick tutorial showing how you can develop a simple and reusable component using ReactJS.

The need

We want a component that can display a quiz.

We need to display a question, allow the user to select one or more answers, validate its answers and go to the next question.

After all questions have been answered we'd like to inform the user of its score and list the questions where he was right or false.

Quiz data (quiz title, questions and answers) can be fetched from an api returning a formatted json (we'll use a local json file for the tutorial).

Getting it Done

ReactJs components must define a render method which returns the html they have to display depending on their properties and state.

For our use case, we will split our component in two components: a main component Quiz representing the whole quiz and a stateless Question component. The Quiz component state contains: quiz data, user answers and current question to display.

Question component properties are: question id, question data and two callback methods: one to add an answer choice and one to validate all choices (and go to the next question).

We first create an empty React Quiz component and export it:

var React = require ( "react" ) ; var $ = require ( "jquery" ) ; var Quiz = React . createClass ( { } ) ; module . exports = Quiz ;

Then the state is initiated as follows:

getInitialState : function ( ) { return { quiz : { } , user_answers : [ ] , step : 0 } } ,

quiz is an object which contains quiz data. user_answers evolves as the user adds its choices to questions. step indicates the current question and is initializated to 0.

We now need to load quiz data from a json file and update the state consequently. For this, we use the componentDidMount method which is called just after the initial render.

When state is updated render method is called again, this time with quiz data.

componentDidMount : function ( quizId ) { $ . getJSON ( "./assets/quiz.json" , function ( result ) { this . setState ( { quiz : result } ) ; } . bind ( this ) ) } ,

Quiz data is stored in a json file formatted as follow:

{ "title" : "Quiz title" , "questions" : [ { "question" : "What is the first question?" , "answers" : [ { "is_right" : true , "value" : "This one" } , { "is_right" : false , "value" : "The next one" } , ... ] } ... ] }

Our component has all the data it needs to display a quiz. We can add a few methods to make it dynamic: go to the next question, add a user answer, compute the current score:

nextStep : function ( ) { this . setState ( { step : ( this . state . step + 1 ) } ) ; } , setAnswer : function ( event ) { this . state . user_answers [ this . state . step ] = this . state . user_answers [ this . state . step ] || [ ] ; this . state . user_answers [ this . state . step ] [ parseInt ( event . target . value ) ] = event . target . checked ; } , isAnswerRight : function ( index ) { var result = true ; Object . keys ( this . state . quiz . questions [ index ] . answers ) . map ( function ( value , answer_index ) { var answer = this . state . quiz . questions [ index ] . answers [ value ] if ( ! this . state . user_answers [ index ] || ( answer . is_right != ( this . state . user_answers [ index ] [ value ] || false ) ) ) { result = false ; } } . bind ( this ) ) ; return result ; } , computeScore : function ( ) { var score = 0 Object . keys ( this . state . quiz . questions ) . map ( function ( value , index ) { if ( this . isAnswerRight ( index ) ) { score = score + 1 ; } } . bind ( this ) ) ; return score ; } ,

Finally we can write the render method. We want our component to display two types of screen; a question with its available answers and the result of a quiz session. Our main render calls one of them based on the current state:

render : function ( ) { if ( ! this . state . quiz . questions ) { return < div > < / div > } return ( < div > < h1 > { this . state . quiz . title } < / h1 > { ( this . state . step < this . state . quiz . questions . length ? ( < Question id = { this . state . step } data = { this . state . quiz . questions [ this . state . step ] } validateAnswers = { this . nextStep } setAnswer = { this . setAnswer } / > ) : ( < div > { this . renderResult ( ) } < / div > ) ) } < / div > ) }

The Question component simply defines a render method using properties given by Quiz component, and calls callbacks when the user interacts with it:

var React = require ( "react" ) ; var Question = React . createClass ( { propTypes : { setAnswer : React . PropTypes . func , validateAnswers : React . PropTypes . func , data : React . PropTypes . obj } , render : function ( ) { var answersNodes = Object . keys ( this . props . data . answers ) . map ( function ( value , index ) { return ( < div > < input id = { "answer-input-" + index } type = "checkbox" value = { value } onChange = { this . props . setAnswer } defaultChecked = { false } / > < label htmlFor = { "answer-input-" + index } > { parseInt ( index ) + 1 + ": " + this . props . data . answers [ index ] . value } < / label > < / div > ) ; } . bind ( this ) ) ; return ( < div > < h4 > { parseInt ( this . props . id ) + 1 + ": " + this . props . data . question } < / h4 > < form > { answersNodes } < br / > < button type = "button" onClick = { this . props . validateAnswers } > Validate answer < / button > < / form > < / div > ) ; } } ) ; module . exports = Question ;

renderResult calls the isAnswerRight method to list result for each question:

renderResult : function ( ) { var result = Object . keys ( this . state . quiz . questions ) . map ( function ( value , index ) { if ( this . isAnswerRight ( value ) ) { return ( < div > { "Question " + index + ": You were right!" } < / div > ) } else { return ( < div > { "Question " + index + ": You were wrong!" } < / div > ) } } . bind ( this ) ) ; }

Installation

You can locally install the project on your computer.

Requirements: npm must be installed on your computer, you can use httpster to serve the files.

git clone git@github.com:bbonny/quiz-react.git cd quiz-react npm install ./node_modules/gulp/bin/gulp.js

In an other, shell launch httpster:

cd quiz-react/dist httpster

You can now access the quiz locally: http://localhost:3333

Github

Full source code can be found here: https://github.com/bbonny/quiz-react/

[joinus]