Routing Params
Now that we have our homepage listing all the posts, let's build the "detail" page—a canonical URL that displays a single post. First we'll generate the page and route:
Now let's link the title of the post on the homepage to the detail page (and include the import
for Link
and routes
):
If you click the link on the title of the blog post you should see the boilerplate text on ArticlePage
:
But what we really need is to specify which post we want to view on this page. It would be nice to be able to specify the ID of the post in the URL with something like /article/1
. Let's tell the <Route>
to expect another part of the URL, and when it does, give that part a name that we can reference later:
Notice the {id}
. Redwood calls these route parameters. They say "whatever value is in this position in the path, let me reference it by the name inside the curly braces". And while we're in the routes file, lets move the route inside the Set
with the BlogLayout
.
Cool, cool, cool. Now we need to construct a link that has the ID of a post in it:
For routes with route parameters, the named route function expects an object where you specify a value for each parameter. If you click on the link now, it will indeed take you to /article/1
(or /article/2
, etc, depending on the ID of the post).
You may have noticed that when trying to view the new single-article page that you're getting an error. This is because the boilerplate code included with the page when it was generated includes a link to the page itself—a link which now requires an id
. Remove the link and your page should be working again:
#
Using the ParamOk, so the ID is in the URL. What do we need next in order to display a specific post? It sounds like we'll be doing some data retrieval from the database, which means we want a cell. Note the singular Article
here since we're only displaying one:
And then we'll use that cell in ArticlePage
:
Now over to the cell, we need access to that {id}
route param so we can look up the ID of the post in the database. Let's update the query to accept a variable (and alias the real query name post
to article
):
Okay, we're getting closer. Still, where will that $id
come from? Redwood has another trick up its sleeve. Whenever you put a route param in a route, that param is automatically made available to the page that route renders. Which means we can update ArticlePage
to look like this:
id
already exists since we named our route param {id}
. Thanks Redwood! But how does that id
end up as the $id
GraphQL parameter? If you've learned anything about Redwood by now, you should know it's going to take care of that for you. By default, any props you give to a cell will automatically be turned into variables and given to the query. "No way," you're saying. Way.
We can prove it! Try going to the detail page for a post in the browser and—uh oh. Hmm:
By the way, this error message you're seeing is thanks to the
Failure
section of our Cell!
It turns out that route params are extracted as strings from the URL, but GraphQL wants an integer for the id
. We could use parseInt()
to convert it to a number before passing it into ArticleCell
, but we can do better than that.
#
Route Param TypesWhat if you could request the conversion right in the route's path? Introducing route param types. It's as easy as adding :Int
to our existing route param:
Voilà! Not only will this convert the id
param to a number before passing it to your Page, it will prevent the route from matching unless the id
path segment consists entirely of digits. If any non-digits are found, the router will keep trying other routes, eventually showing the NotFoundPage
if no routes match.
What if I want to pass some other prop to the cell that I don't need in the query, but do need in the Success/Loader/etc. components?
All of the props you give to the cell will be automatically available as props in the render components. Only the ones that match the GraphQL variables list will be given to the query. You get the best of both worlds! In our post display above, if you wanted to display some random number along with the post (for some contrived, tutorial-like reason), just pass that prop:
And get it, along with the query result (and even the original
id
if you want) in the component:Thanks again, Redwood!
#
Displaying a Blog PostNow let's display the actual post instead of just dumping the query result. We could copy the display from the articles on the homepage, but that's not very reusable! This is the perfect place for a good old fashioned component—define the display once and then reuse the component on the homepage and the article display page. Both ArticlesCell
and ArticleCell
will display our new component. Let's Redwood-up a component (I just invented that phrase):
Which creates web/src/components/Article/Article.js
(and corresponding test and more!) as a super simple React component:
You may notice we don't have any explicit
import
statements forReact
itself. We (the Redwood dev team) got tired of constantly importing it over and over again in every file so we automatically import it for you!
Let's copy the <article>
section from ArticlesCell
and put it here instead, taking the article
itself in as a prop:
And update ArticlesCell
and ArticleCell
(note the plural and singular naming) to use this new component instead:
And there we go! We should be able to move back and forth between the homepage and the detail page. If you've only got one blog post then the homepage and single-article page will be identical! Head to the posts admin and create a couple more, won't you?
If you like what you've been seeing from the router, you can dive deeper into the Redwood Router guide.
#
SummaryTo recap:
- We created a new page to show a single post (the "detail" page).
- We added a route to handle the
id
of the post and turn it into a route param, even coercing it into an integer. - We created a cell to fetch and display the post.
- Redwood made the world a better place by making that
id
available to us at several key junctions in our code and even turning it into a number automatically. - We turned the actual post display into a standard React component and used it in both the homepage and new detail page.