The Question
component currently shows the question but doesn't give the user any way to respond. You want to present two radio buttons for a true/false question. You want a text input for a fill-in-the-blank question. For the time being, these are the only two types of questions you wish to support. Sometime later you'll come back and add multiple choice, matching, ordering, and other advanced question types. One step at a time, you remind yourself.
You also want a button below the inputs that either advances to the next question or, if the user is on the last question, submits all the responses to some web service.
Both the response inputs and the button will vary depending on the question. That means you need conditional rendering. To keep the JSX somewhat clean, you decide to embed a couple of variable references that will hold the components for the inputs and the button:
export function Question(props) {
// ...
let inputs;
let button;
return (
<div className="question-page">
<div className="current-question">
<h2>{index + 1}. {question.title}</h2>
<p>{question.prompt}</p>
{inputs}
{button}
</div>
</div>
);
}
The button is simpler, so you start with it. You use the question index to figure out if the user is on the last question or not. If not, you make a next button that updates the path to point to the next question using navigate
. If the user is on the last question, you just present a submit button that does nothing because you're not ready to think about what to do with the responses.
You write a conditional state that assigns the appropriate button JSX to the button
variable:
// ...
import {useParams, useNavigate} from 'react-router-dom';
export function Question(props) {
// ...
const navigate = useNavigate();
let button;
if (index < quiz.questions.length - 1) {
button =
<button
onClick={() => navigate(`/quiz/${quiz.slug}/question/${quiz.questions[index + 1].slug}`)}
>Next</button>;
} else {
button =
<button>Submit</button>;
}
// ...
}
Try clicking on the next button. Does clicking advance you through the questions until you hit the last one?
For the inputs, you write a similar conditional statement. If the question is true/false, you assign to inputs
two radio buttons that are in the same group. If the question is a fill-in-the-blank, you assign to inputs
a text input. The conditional isn't too ugly:
export function Question(props) {
// ...
let inputs;
if (question.type === 'true-false') {
inputs =
<div className="response">
<label>
<input type="radio" name="group" />
True
</label>
<label>
<input type="radio" name="group" />
False
</label>
</div>;
} else if (question.type === 'blank') {
inputs =
<div className="response">
<input type="text" />
</div>;
}
// ...
}
In both cases, the inputs are wrapped up in a container with class response
. You add some margin around the response and the question and put each label on its own line by adding these styles to App.css
:
.response {
margin: 40px 0;
}
.current-question {
margin: 40px;
}
.current-question > h2 {
margin: 0;
}
label {
display: block;
}
Try starting the quiz afresh. Do you see the inputs? What happens when you interact with them? What happens you visit the next question? Likely you are seeing that there are some issues you need to fix. The responses linger as you jump between questions. You decide to let that stay broken for a while. You'd rather invest some time building a question navigator so the user can return to earlier questions or complete them out of order.