This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Building a React.js App: componentWillReceiveProps and React Router

5:18 React lesson by

In this lesson, we’ll learn how React Router uses the componentWillReceiveProps life cycle method in order to give you any route changes that occurs in your application. We’ll leverage this to fetch new data about different users when the user requests that data.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

In this lesson, we’ll learn how React Router uses the componentWillReceiveProps life cycle method in order to give you any route changes that occurs in your application. We’ll leverage this to fetch new data about different users when the user requests that data.

Avatar
Transformers

Hi Tyler, I' m trying out the tutorial, and so far it has been a good experience learning ReactJS. However for some reason the data is not unbinding.I and using the new version of React and when I try changing the GitHub username only the username changes but not the entire data. If I enter a new username then it loads the previous data and not a new data.

In reply to egghead.io
Avatar
Tyler

It's hard to tell from your description what the error could be. Have you checked out the Github version? It's (as of yesterday) been updated with all the latest versions of everything. https://github.com/tylermcginnis/github-notetaker-egghead

In reply to Transformers
Avatar
egghead.io

The lesson video has been updated!

Avatar
Alan

Can you explain what the role of componentWillUnmount is in this app? I commented it out and was able to have the app function perfectly without it. Maybe it's because the component never unmounts? I'm not sure in what situation a component would unmount or even how to unmount one.

Avatar
Tyler

Great observation! Usually the role of componentWillUnmount is to remove/clean up any listeners you've established whether that's with flux, Firebase as in our example, or any other listeners. If you take out componentWillUnmount, you'll no longer be removing the listener to Firebase. Now, this isn't a HUGE deal, but what if that component mounts again, then you've created another listener. Now all of a sudden you have two listeners to firebase. And if this happens again, 3 listeners etc etc. So componentWillUnmount is mostly used to remove listeners (therefor protecting against memory links etc)

In reply to Alan
Avatar
Ryan

When I use console.log() anywhere in the Profile component or any of it's children, the result is logged 4 times. Assuming that's not supposed to be the case, are there any tricks to figuring out why that is happening? Thx!

Hmmm. I actually just downloaded your repo and the behavior is the same. Perhaps this is normal re-rendering?

In reply to egghead.io
Avatar
Tyler

React will (efficiently) re-render whenever a component receives new props or state. Check out shouldComponentUpdate to tell React specifically when it should re-render.

In reply to Ryan
Avatar
Maximilian Patan

I've also experienced this behavior. This happens when prefixing the "/" to profile in SearchGithub.js as instructed in this lesson. Trying to figure out why this is happening. May be a version issue.

Tyler - wonderful series btw. It's a great tutorial with some meat to it. Your explanations are clear and well thought-out. Please do other tutorials - you have a talent for communicating complex concepts with clarity. Alliteration not intended - just worked out that way :)

In reply to Transformers
Avatar
Maximilian Patan

Problem solved - componentWillReceiveProps was not being called because of a spelling error. duh.

In reply to Maximilian Patan
Avatar
Tyler

Thank you Max - that truly means a lot.

Also, I can't tell you how many times I've mis-spelled componentWillReceiveProps as well :)

In reply to Maximilian Patan
Avatar
Jin

I have been following this tut this far, thank you Tyler for your hard work. Correct me if i am wrong, since the Rounter History has deprecated, i am using context router to replace it, and the search function is ok and you dont need the componentWillReceiveProps to unbind and rebind incoming props to render the new page?

Avatar
marlonjfrausto

I just completed this video for the tutorial but some properties in a user's profile return as 00 within the unordered list. did anybody else encounter this issue? any ideas/hints on what may be going on and how to fix it?
Not seeing any errors in the console so don't really know how to debug/address issue.
BTW: AWESOME tutorial tyler, can't even begin to tell you how helpful this has been !

In reply to Tyler
Avatar
Priyanka Malviya

Hello Tyler, Awesome job on this tutorial! My app works fine but the app hangs when I try to search a second user. I tried to look carefully into the video again to find out the error but cannot. Could you please help? My code is here: https://github.com/priyankamalviya/githubnotes

In reply to Tyler
Avatar
Kylan Hurt

For some reason when I try to access jakelingwall's profile I get the following error in my console:
bundle.js:1178 Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {text}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of NotesList.

Otherwise the lesson and code seems to work fine.

Avatar
Andrewsh86

There seems to be a bug after introducing the change of "profile/" to "/profile/" in SearchGithub.js.

After completing all of the code in this lesson, when you search for two users, they'll show up and load fine, but the third one will come back as an undefined user and the url will be something like this:

/public/index.html?#/profile/undefined?_k=kb9dpq

I tried searching andrewsh86, then tenderlove, then SamyPesse

If I replace the "undefined" in the url with the correct username, it works fine. There's something buggy happening with the SearchGithub.js and not pushing to the history correctly. I verified that changing it back to be "profile/" fixes this problem, but reintroduces the issue of loading the page completely again.

One thing you may have noticed as you were playing around with this is, every time we go to a new route, we're refreshing the whole view. If I go to another user, it refreshes the view. That's obviously not what we want, because we're building a single-page application and we don't want the whole view to refresh.

That's because we have a typo or a bug in one of our previous videos. Go ahead and go to the SearchGitHub component. Right here, instead of being a profile/, we want it to be /profile/, and then the username. Once we do that, then we should get something like this. I'm going to go home, I'll type in a new username, and there we go.

components/SearchGithub.js

handleSubmit: function() {
  var username = this.usernameRef.value;
  this.usernameRef.value = '';
  this.history.pushState(null, "/profile/" + username);
}

You'll notice here, if I type in a new username now, it doesn't refresh the whole view. This data is still the same, even though we're going to a new route. The reason for that is because we're receiving new props into our components, but we're not doing anything with those props. React gives us another handy little lifecycle method that we could hook into, which is called componentWillReceiveProps.

Notes/Profile.js

componentWillReceiveProps: function(nextProps){

}

What this does is, whenever our Profile component receives new props, this callback function is going to get invoked. What's nice is, whenever react-router changes routes, because our routing is going through props, this function will get invoked, and then we'll have those new props.

Let's go ahead and see this. What I'm going to do is, inside of componentWillReceiveProps, let's just console.log. The next Props are with the nextProps.

Notes/Profile.js

componentWillReceiveProps: function(nextProps){
  console.log('The next props are', nextProps);
}

What we should see here is, if I refresh this and go to a new username, we get these new props and, under params, we have the username.

nextProps

Let's go ahead and change up our component a little bit. What I'm going to do is let's go ahead and make an init function. What's going to happen is we're going to call this data or this chunk of code when the component mounts, but also we want to set up a listener to the new user whenever we receive new props.

Let's go ahead and I'm going to copy this code and put it into my init function.

Notes/Profile.js

init: function(){
  var childRef = this.ref.child(this.props.params.username);
  this.bindAsArray(childRef, 'notes');

  helpers.getGithubInfo(this.props.params.username)
    .then(function(data){
      this.setState({
        bio: data.bio,
        repo: data.repos
      })
    }.bind(this))
},

Then, in componentDidMount, I'm gong to call this.init.

Notes/Profile.js

componentDidMount: function(){
  this.ref = new Firebase('https://github-note-taker.firebaseio.com/');
  this.init();
}

We don't want to query this.props.params.username, because what's going to happen is, when this init function runs, if we call it in componentWillRecieveProps, we're not giving it the new props.

If I'm on my own profile, TylerMcGinnis, if we type in a new user, this.props.params.username is still going to be TylerMcGinnis. What we want to do is let's go ahead and have this init function take in a username, and then we'll switch out all of these for that username.

Notes/Profile.js

init: function(username){
  var childRef = this.ref.child(username);
  this.bindAsArray(childRef, 'notes');

  helpers.getGithubInfo(username)
    .then(function(data){
      this.setState({
        bio: data.bio,
        repo: data.repos
      })
    }.bind(this))
},

Then what we can do is, whenever the component receives props, we're going to go ahead and pass this.init(nextProps.params.username).

Notes/Profile.js

componentWillReceiveProps: function(nextProps){
  this.init(nextProps.params.username);
}

Also, when our component mounts, we're still going to pass it this.props.params.username.

Notes/Profile.js

componentDidMount: function(){
  this.ref = new Firebase('https://github-note-taker.firebaseio.com/');
  this.init(this.props.params.username);
}

Init is now taking in the username, whether it's from the params itself or, when we receive new props, those nextProps coming in.

Let's go ahead and see if this works. Everything looks good. Webpack is running. Hit refresh. Let's go to Spencer's profile. This.state.notes is already bound to a Firebase reference.

The reason this is being thrown is because, in our componentDidMounts, we run init, and that comes in here and binds to our notes property on our state. Firebase isn't going to let us bind to multiple things, which make sense.

What we're going to do is, inside of componentWillReceivesProps, before we call init again, let's go ahead and call this.unbind(notes).

Notes/Profile.js

componentWillReceiveProps: function(nextProps){
  this.unbind('notes');
  this.init(nextProps.params.username);
}

Now when we receive props, we're going to unbind what we bound to in our component when it mounted, and then we're going to rebind to the new username.

Let's see if this works now. TylerMcGinnis, there we go. Our data loads, and we can go to any username we want here.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?