Clicking on the links in the Quizzes
component changes the app's path to something like /quiz/state-capitals
. You want the Quiz
component to extract the slug from this path and use it to get the quiz from the catalog. React Router will do this extraction for you if you announce that the /quiz
route expects a parameter:
<Route path="/quiz/:quizSlug" element={<Quiz />} />
Try changing this route in your App
component. This syntax is exactly what you saw with endpoints in Express. And you are likely to see it in other web technologies too.
Once the parameter is declared in the path, you may call useParams
in the element that the route renders. The value that useParams
returns is a collection of key-value pairs indexed by the parameter names. For the state capitals quiz, the parameters object looks like this:
{
quizSlug: 'state-capitals'
}
Inside Quiz
, you import useParams
, get the parameters object, and use the slug to look up the quiz from the catalog:
import {useParams} from 'react-router-dom';
import {quizzes} from './catalog';
export function Quiz(props) {
const params = useParams();
const quiz = quizzes[params.quizSlug];
return (
<div className="quiz-page">
<h2>{quiz.title}</h2>
<p>{quiz.description}</p>
</div>
);
}
A route can have multiple parameters. You add a new route for the Question
component in App
and declare it to have two parameters: a quiz slug and a question slug. You place it right above the fallback Redirect
route:
function App() {
return (
<div className="App">
<Routes>
{/* ... */}
<Route path="/quiz/:quizSlug/question/:questionSlug" element={<Question />} />
</Routes>
</div>
);
}
To make the significance of each parameter clear, you name each one using static text like quiz
and question
. You will bring suffering if you do not label the parameters. A path of the form /load/34/91/67/true
is impenetrable.
Inside Question
, you use the extracted quiz and question slugs to look up the quiz and question:
import {useParams} from 'react-router-dom';
import {quizzes} from './catalog';
export function Question(props) {
const params = useParams();
const quiz = quizzes[params.quizSlug];
const index = quiz.questions.findIndex(question => question.slug === params.questionSlug);
const question = quiz.questions[index];
// ...
}
You use the findIndex
command instead of find
so that you get the question's serial number. In the JSX, you show the question's serial number, title, and prompt:
export function Question(props) {
// ...
return (
<div className="question-page">
<div className="current-question">
<h2>{index + 1}. {question.title}</h2>
<p>{question.prompt}</p>
</div>
</div>
);
}
There's no way for the user to respond to the question currently. Before fixing that, however, you need a way for the user to load the first question of a quiz without typing the slug of the first question in the URL.