React router and the lifecycle methods

We start this post from where we left off in the last one. We created a silly app with a read only view and a view with a button. Clicking the button toggles the style of the text block. Today we want to extend this app with the ability to update the style via the URL and hence, remove this from the state and count the number of clicks on the button.

The former is rather simple as it is described in detail on the React router Github page. The latter, however, is more interesting as we need to take a dive in the details of the React lifecycle methods.

The code for this post is available on Bitbucket (checkout version 1.2). As before, make sure to run npm install (to install all dependencies) and then npm run start to compile the app with webpack. Visit http://localhost:7770/ afterwards to see the app in action. Let’s write some code!

Using URL parameters

As discussed, the clickable view has two styles. Hence we have to define two routes. But wait a minute, what if it had 10 or maybe 100 styles? Would we have to define 10 or even 100 routes in index.js?

No. By creating the route as

<Route path="/clickable/:turn" component={Clickable}></Route>

the turn becomes a variable. In our case we will use the following links

http://localhost:7770/clickable/0 and http://localhost:7770/clickable/1 and inside the <Clickable /> component we read the URL parameter and set the correct style based on it. Reading the URL parameter is rather straightforward as it is available in the params object which is passed via props (by default when using React router). Hence, inside the Clickable component, we read it from props like this

const turn = parseInt(this.props.params.turn);

Now the constant turn is either 0 or 1 and we can use it to setup the correct styles. Now we have removed the ‘turn’ from the state and the Clickable component has become stateless.

Count the number of clicks

However, since we wish to count the number of clicks on the button, the state will return! But we would like to keep the <Clickable /> component stateless (as this is good practice) and thus we set state on the <App /> component. Initially there are no clicks on the button yet. The initial state for the counter is simply 0.

The next thing we need to do is to propagate the state via props towards the Clickable component. This took a while to get right, but we may use the React.cloneElement function for this. Previously we used this function to render the correct child component and now we extend it to also pass props to the child. The syntax in our case is this:

Basically the only thing that changed is that we added a key: value pair to the object that is passed as the second argument to React.cloneElement. If we do it this way the prop is available in both the <Useless /> and the <Clickable /> components via this.props.count. As you can see in the code for the <Clickable /> component

the count is read from the props and displayed.

So far so good! But now it becomes interesting as we need to update the number of clicks when the user clicks the button. But when he does, the app is rerendered due to the change of the URL. So a simple callback function that is passed as prop won’t work out here. Luckily the React lifecycle methods come to the rescue. The question is, which one do we use? Reading the docs is key and there we find this:

componentWillReceiveProps

Invoked when a component is receiving new props. This method is not called for the initial render. Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.

This is exactly what we need! Once the button is clicked the URL is changed and our <App /> component is rendered with new props and it seems that we can simply update the counter in the state.

Wait a minute!

Just clicking the button in the <Clickable /> component is not the only action that triggers the update of the URL. What about clicking the ‘Silly App’ link? Or the links from the startscreen? These clicks also update the URL and componentWillReceiveProps are called again. This means that just implementing our state update inside this lifecycle hook results in incorrect code: the number of clicks is updated too often: on all URL changes.

So we read the docs again and print the new props and the old props to the console. It turns out that only when clicking the button both this.props.params.turn and nextProps.params.turn are not undefined. Adding this check inside the componentWillReceiveProps function makes sure we only update the counter when the button is clicked. Here is the code snippet:

Recap

Today we did a couple of things to make our Silly App a little more interesting. First we removed the ‘turn’ from the state. Instead, the styling of the <Clickable /> component was changed based on the URL and we changed the route.

Apart from this we wished to count the number of clicks on the button and display it on the button. We added it to the state and updated the state in the componentWillReceiveProps function. This is one of the React lifecycle methods which are very powerful to do things just before or after the (re)rendering of your app.

Sidenote

The changes we made today by removing the ‘turn’ from the state an adding it to the URL were just to show how this works with React router. In a real app care must be taken when designing the URL’s and of course the state itself.


Mijn Twitter profiel Mijn Facebook profiel
Pim Hooghiemstra Webdeveloper and founder of PLint-sites. Loves to build complex webapplications using React! Latest post
Did js-beautify break the internet?

Leave a Reply

Your email address will not be published. Required fields are marked *