Routes with Parameters

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.