How to Pass Props Through React-Router Path Links

crashdaddy
6 min readJun 21, 2020

Introduction

So you’ve got your amazing app all refactored into components and you’ve set up react-router to provide you a path to all your different pages.

That’s great!

But then you find out that one of your pages needs to redirect the user to another page along with some props that they’ll need once they get there!

You don’t want to use a global state manager like Redux, because who knows why you do the crazy things that you do, but you still need a way.

I can tell by the amount of people I ran into with the same problem while I was researching my own solution that this comes up quite a bit in development.

The Situation

Let’s use the example I ran into to figure out what all we need to do.

For my capstone project at good ‘ol Austin Coding Academy (what problem does it solve? Boredom!) I’m building that fun puzzle game you see in the top image. It’s like a 2-D NxN Rubik’s Cube version of solving scrambled up photos.

I’m using Unsplash for the API to get all the photos, because why bother uploading and storing my own when there are 2 million of them just eager to be used?

The thing about Unsplash is they are very strict on attribution. You have to always show whose picture you’re using and provide links to their social media.

That’s a key element we’ll get to in a minute.

So you build a nice page where the user can select a photo they want to use as a puzzle, and it looks something like this:

That’s some ripe puzzle-solvin’ material right there

Then when the user selects one of those photos they’re redirected — or should I say, routed — to another page where that photo has been carved up and ready for solvin’.

I could solve this puzzle all day

Here’s my router setup:

const Router = () => {
return (
<Switch>
<Route exact path ="/" component={PuzzlePicker} />
<Route path="/search/:query?" component={PuzzlePicker} />
<Route path="/puzzle/:id" component={App} />
</Switch>
)
}

You can see where I’ve sent the puzzle’s ID in the url, both in that snippet of code and in the address bar of the picture above it.

Admit it. You didn’t even notice there was an address bar in that picture.

But here’s the thing about Unsplash. They have a specific link they want you to use to hotlink the image so they can trace all their uses. So you can’t just embed the ID into a url and use that as the image source. It’s a whole big thing with them.

The Solution

Of course there’s always the obvious solution: why don’t you just get the ID number from the URL and call the API using that ID number like this:

class Puzzle extends React.Component {
state = {
puzzleDetails: null
}
componentDidMount () {
const { photoId} = this.props.match.params
fetch(`https://api.unsplash.com/photos/${photoId}`)
.then((puzzleData) => {
this.setState(() => ({ puzzleData }))
})
}
render() {
...
}
}

Well, that would be great, and ideally that’s the solution we’d want to implement.

The Nother Problem

But don’t forget the rate limits!

With your cutting-edge infinite scroller algorithm on the search page and the fact that your app is so amazing that 11ty-thousand people want to use it at the same time, you can breeze through 50 calls in the first few minutes, let alone taking a whole hour!

Nope. We gots to get creative over here. Let’s put on our thinking caps.

The Nother Solution

It turns out that react-router has a little-known, oft-overlooked feature with it that allows you to send a mini-state through the path as a prop.

Because url parameters are great for sending something simple like an ID or a query string or a page number, but what if we need to send something a little more complicated, like an object containing all the details about a puzzle from an API call we’ve already made and don’t want to make again?!

That’s where props.location.state comes in.

When you embed your Link component you can add a “state” object like this:

<Link to={{ pathname: `/puzzle/${puzzle.id}`, state: { puzzle} }} >

So now you’re calling the router with the path to the puzzle, with the appropriate ID, but you’re also sending along another item called “state” that represents an object called “puzzle” which is actually all the API results for that puzzle from the search page!

[{"id":"M8Q1d953AdE",
"created_at":"2019-08-27T02:59:55-04:00",
"updated_at":"2020-05-14T01:13:54-04:00",
"promoted_at":null,"
width":3561,
"height":3554,
"color":"#F9E051",
"description":"whisky art",
"alt_description":"bottle on wood",
"urls":{
"raw":"https://images.unsplash.com/photo-1566889034273-0b5ec95bb736?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE0MzIzN30",
"full":"https://images.unsplash.com/photo-1566889034273-0b5ec95bb736?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0MzIzN30",
"regular":"https://images.unsplash.com/photo-1566889034273-0b5ec95bb736?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjE0MzIzN30",
"small":"https://images.unsplash.com/photo-1566889034273-0b5ec95bb736?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&ixid=eyJhcHBfaWQiOjE0MzIzN30",
"thumb":"https://images.unsplash.com/photo-1566889034273-0b5ec95bb736?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&ixid=eyJhcHBfaWQiOjE0MzIzN30"},
"links":
{"self":"https://api.unsplash.com/photos/M8Q1d953AdE",
"html":"https://unsplash.com/photos/M8Q1d953AdE",
"download":"https://unsplash.com/photos/M8Q1d953AdE/download",
"download_location":"https://api.unsplash.com/photos/M8Q1d953AdE/download"},
"categories":[],
"likes":5,
"liked_by_user":false,
"current_user_collections":[],
"sponsorship":null,
"user":{
"id":"55whWhfRi0E",
"updated_at":"2020-05-09T02:54:05-04:00",
"username":"nepalbuda",
"name":"Garo Uzunyan",
"first_name":"Garo",
"last_name":"Uzunyan",
"twitter_username":null,
"portfolio_url":null,
"bio":null,
"location":null,
"links":{
"self":"https://api.unsplash.com/users/nepalbuda",
"html":"https://unsplash.com/@nepalbuda",
"photos":"https://api.unsplash.com/users/nepalbuda/photos",
"likes":"https://api.unsplash.com/users/nepalbuda/likes",
"portfolio":"https://api.unsplash.com/users/nepalbuda/portfolio",
"following":"https://api.unsplash.com/users/nepalbuda/following",
"followers":"https://api.unsplash.com/users/nepalbuda/followers"},
"profile_image":{
"small":"https://images.unsplash.com/profile-fb-1566888726-7698a4b5904a.jpg?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=32&w=32",
"medium":"https://images.unsplash.com/profile-fb-1566888726-7698a4b5904a.jpg?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=64&w=64",
"large":"https://images.unsplash.com/profile-fb-1566888726-7698a4b5904a.jpg?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=128&w=128"},
"instagram_username":null,
"total_collections":0,
"total_likes":1,
"total_photos":9,
"accepted_tos":true},
"exif":
{"make":"Canon",
"model":"Canon EOS 6D",
"exposure_time":"1/500",
"aperture":"5.0",
"focal_length":"118.0",
"iso":125},
"location":{
"title":null,
"name":null,
"city":null,
"country":null,
"position":{
"latitude":null,
"longitude":null}
},
"views":69497,
"downloads":203
}

Try shoving that all into a URL and you turn into early ‘00s Google.

Now you’re playing with power! Because you already had all that data on the search page, and when you send it through the react-router Link as a “state” object you can access the same data on the puzzle page without having to make a new API call!

And here’s how you retrieve the data from the destination page:

Remember our new friend props.location.state? Now in our Puzzle class from above we can just call all the data without a new fetch, like this:

class Puzzle extends React.Component {
state = {
puzzleDetails: null
}
componentDidMount () {
const { photoId} = this.props.match.params
this.setState({
puzzleDetails: props.location.state.puzzle
})
render() {
...
}
}

It was amazing. Everybody cheered and clapped. You got a corner office and a company car for your innovativity.

The Nother Nightmare

Then you remember that one of the main reasons we use react-router is so that certain URLs can be built to focus on individual elements of your app without having to navigate to them each time.

Like how cool would it be if people could just type in a link:

https://puzlr.herokuapp.com/XKCD69BDSM420

and it just goes to the particular puzzle they — or some friend they want to send the link to — can just jump right in to and start solving?

I’m thinking pretty cool.

However, by sending the puzzle data through the router as a prop from the search page, if one were to just enter that URL and go directly to that page none of the data would be there!

The page would devolve into a fiery mess of cryptic error messages.

Try explaining this to the boss

You’d be standing in your new office on the sidewalk, holding a box with your personal items from your desk while watching them drive off in your new company car.

But I’ve got your back again!

The Nother Solution

This is a simple fix.

In your component that receives the data object through the react router path, just perform a test to see if that data exists, silly.

That way, if the data is sent through as a prop you don’t have to make an extra API call and erode your precious limited bandwidth from Unsplash. If it’s not, then you can just use the ID number from the URL and make the API call.

Now the boss is trying to get you to come back, but you already found another job where they respect your fortitude.

And you’re using your new fat raise to buy me coffee

Thanks, internet stranger!

--

--

crashdaddy

4a 75 73 74 20 61 6e 6f 74 68 65 72 20 63 6f 6d 70 75 74 65 72 20 6e 65 72 64 20 77 69 74 68 20 61 20 62 6c 6f 67