This is Part two of out Quiz App Tutorial in React. Please see Part 1 if you have not.
In this part, we would put together the various functions, states and pieces that make up the quiz. So let’s get started!
The various part of of the code includes
- States
- loadQuiz()
- nextQuestionHandler()
- checkAnswer()
- finishHandler()
- componentDidMount()
- componentDidUpdate()
- render() method
The States
First, we would try to understand the states of the component. I have outlines the 6 different states i think we would need. So copy and paste the code. I will not speak on this as the comment are very clear
state = { userAnswer:null, //current users answer currentIndex:0, //current questions index options: [], //the four options quizEnd: false, //determines if it's the last question score: 0, //holds the score disabled: true // determines the status of the buttons }
The loadQuiz() Function
This function is responsible for loading a single question based on the currentIndex state
So it first gets the current index which is an integer value. Then it reads the question on that index from the QuizData component. Finally, using the values it read, it updates the question, options and answer state and returns them. Of course, this changes in state is reflected in the UI as well.
//Component that holds the current quiz loadQuiz = () => { const {currentIndex} = this.state //get the current question index this.setState(() => { return { question: QuizData[currentIndex].question, options : QuizData[currentIndex].options, answer: QuizData[currentIndex].answer } } ) }
The nextQuestionHandler()
This function is executed when the user clicks on the “Next” button.
First, it obtains the user’s answer, the correct answer and the score. Then it increments the currentIndex. Finally, it checks if the userAnswer is equal to the correct answer and if so, increments the score. I hope this is clear as well.
So you can type it out yourself.
nextQuestionHander = () => { const {userAnswer, answer, score} = this.state this.setState({ currentIndex: this.state.currentIndex + 1 }) //Check if correct answer and increment score if(userAnswer === answer){ this.setState({ score: score + 1 }) } }
The checkAnswer() Function
This function executes when the user select an options. It simply sets the userAnswer state and then enables the next button (disabled = false)
//Check the answer checkAnswer = answer => { this.setState({ userAnswer: answer, disabled:false }) }
The finishHandler() Function
This function simply checks if the quiz has gotten to the last question. Then it sets the quizEnd state to true.
finishHandler =() => { if(this.state.currentIndex === QuizData.length -1){ this.setState({ quizEnd:true }) } }
componentDidMount() and componentDidUpdate()
The first one, componentDidMount() would simply call the loadQuiz() function to load the first question once the component mounts. In this case, the currentIndex has been initialized to 0.
componentDidUpdate() however would check if the state has changes, and if so, it loads the next question. As it loads a new question, it also sets the disabled state to true. This means that the next button would be disabled until the user selects an option.
The render() method
This is the most involving method. But I would try to explain it very clearly. I also recommend you follow with the video for a clearer understanding. This is the method that actually display the question to the output
The first thing it does is to get all the necessary states that it requires.
Next, it checks if the quiz has gotten to the last question. If so, it renders the results text, then tells the user his score and then displays the correct answers.
However, if the last question has not been reached, it displays the question at the current index together with the four options. Then it responds to the following events:
- when an answer is selected: in this case, it executes the checkAnswer() function
- when the Next button is clicked: call the nextQuestionHandler()
Also note the following:
- the four options are displayed using the map method
- conditional formatting applied to the answer buttons
- the className of the Next button
Other aspects of the code is explained in the video
render() { const { question, options, currentIndex, userAnswer, quizEnd} = this.state //get the current state if(quizEnd) { return ( <div> <h1>Game Over. Final score is {this.state.score} points</h1> <p>The correct Answers for the quiz are</p> <ul> {QuizData.map((item, index) => ( <li className='ui floating message options' key={index}> {item.answer} </li> ))} </ul> </div> ) } return ( <div> <h2>{question}</h2> <span>{`Question ${currentIndex} of ${QuizData.length -1}`}</span> {options.map(option => ( //for each option, new paragraph <p key={option.id} className={`ui floating message options ${userAnswer === option ? "selected" : null} `} onClick= {() => this.checkAnswer(option)} > {option} </p> ))} {currentIndex < QuizData.length -1 && <button className="ui inverted button" disabled = {this.state.disabled} onClick = {this.nextQuestionHander} >Next Question</button> } {currentIndex === QuizData.length -1 && <button className="ui inverted button" disabled = {this.state.disabled} onClick = {this.finishHandler} >Finish</button> } </div> ) }
So complete and test the application. As I mentioned, ensure to follow with the video. Also feel free to reach me if you have any challenges!
Congrats for getting this far!