Updating a Parent Component's State in React from a Child Component
It's time for the moment of truth, we know that we have a login component that is working.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

It functions properly, meaning that it communicates with the outside service, and it logs us in and creates the session, stores the cookie in our browser, and performs those kinds of tasks.

But it's not really doing anything as far as following the standard kind of usage. We would expect with a login component that you would log in and then it would redirect us to the homepage or some other page and then give us some additional access points, but for right now, it's not doing that, so in this guide, we are going to do that.

We're gonna start at the app component, we've already added our render prop, and we've made it possible for all of that to work. Then from there, we are going to go to the auth component. We're gonna have it accept the props, work with it, and then pass props to the login component.

Now, I know that may seem a little bit convoluted if you've never done it before, and this is probably as far as you'd ever want to pass props. You never usually want to pass props more than a child and then a second child or a grandchild type of component. Anything further than that starts to get too messy, but for this case, this will work properly.

So let's start this out. I'm going to give us a visual display here in the navbar just for debugging purposes for our current login state, so let's open up the app component here, and right below the navigation container, let's add an h2 tag that says this.state.loggedInStatus, close off that h2 tag, hit save and now by default, we have that as NOT_LOGGED_IN. So that is working properly, that's giving us everything that we'd expect.

app.js

<h2>{this.state.loggedInStatus}</h2>

large

Now, let's move to the auth component. So I'm gonna open up auth here, and this is where we're gonna spend the majority of our time here because it is going to be the intermediary. It's going to communicate up to the app component, but it's also going to pass data back and forth with the login component.

So the very first thing I'm gonna do is to create a constructor here. It does accept props because we're getting those render props now from the route, so we need to say super props. And then from there, we're going to have a couple of methods that we need to bind.

So I'm gonna create two methods, two handlers, in here, and these are gonna be the handlers that are gonna have the goal of two things. They're going to update the props that we received, so they're going to update the render props of handleSuccessfulLogin and handleUnsuccessfulLogin, and then they are gonna be the props that are passed down to the login component.

So I'm gonna say this.handleSuccessfulAuth, and the reason why I'm not saying login is just so we don't get confused on the naming. We have the naming of handleSuccessfulLogin here, I don't want to have both of them share the same name, so I'll say this.handleSuccessfulAuth, and we're gonna bind that to handleSuccessfulAuth bind to this so that it has access. Remember, we have to do that every time we're passing a method down through props, and then we're gonna have the same thing, handleUnsuccessfulAuth, handleUnsuccessfulAuth, and now let's actually create those two methods.

auth.js

export default class Auth extends Component {
  constructor(props) {
    super(props);

    this.handleSuccessfulAuth = this.handleSuccessfulAuth.bind(this);
    this.handleUnsuccessfulAuth = this.handleUnsuccessfulAuth.bind(this);
  }

So handleSuccessfulAuth is going to do a couple of things, but its primary goal is just going to be to update the props that we receive, so this handleSuccessfulLogin, all we're going to do here, if you look up at the method definition, it doesn't expect any arguments, as soon as we call that, it is gonna change the state to logged in.

So what I can do is say this.props, so we call this just like any other prop starting early back in the course where we created our page title prop. That was a very basic, just we're passing the string from one component to another. Well, this is really the same concept, we're passing a function this time instead of a string, that is the only difference.

So I'm gonna say this.props.handleSuccessfulLogin, and then I'm just going to say that that's it. I'm just gonna call the function, and then it will run in that parent. Now, the other thing I'm gonna do, this is gonna be pretty cool, this is where we're gonna perform the redirect. So it would be kind of confusing if the user hit login, it logged him in, but nothing changed.

I want to redirect them to the homepage, and so the way you can do that with react router is by saying this.props.history, so what we're doing is we're looking into the history of our browsing, not the browser history in terms of any other websites that you go to, we don't have any access to that.

This gives us access to the history from the first time that a user accessed the site or accessed the application. Where did they navigate to? What other routes did they hit? Each time they hit a new route, it adds onto the history, and so what we can say is this.props.history, and then push, and then simply as a string, a backslash.

handleSuccessfulAuth() {
  this.props.handleSuccessfulLogin();
  this.props.history.push("/");
}

What this is going to do is it's going to push them to the root route, which is the homepage. So that is handleSuccessfulAuth.

Now let's also take care of handling UnsuccessfulAuth, this one's even more basic. In this case, we're simply gonna be calling the props that we passed in already of handle unsuccessful login. So this.props.handleUnsuccessfulLogin, and that is all we're going to do.

I may or may not have a spelling mistake on this where it's unsuccessful. I usually do not like to do the capital S there, so let's just see. It does look like I did that in each spot, and so let me just update that in every spot. Yeah, and that would have created a definite bug 'cause notice, I didn't call it down here, so that would have been a problem. But now that is all nice and fixed, and we should be good to go, if it did happen anywhere else, we'll find it out later.

handleUnsuccessfulAuth() {
  this.props.handleUnsuccessfulLogin();
}

Right now, I'm really concerned primarily with the successful login, and in another guide will take care of what happens when that is unsuccessful. Okay, so this is all saved, so now that we have all of this, this is all that we need to do in order to pass data back up the chain.

auth.js

  this.handleSuccessfulAuth = this.handleSuccessfulAuth.bind(this);
  this.handleUnsuccessfulAuth = this.handleUnsuccessfulAuth.bind(this);
}

handleSuccessfulAuth() {
  this.props.handleSuccessfulLogin();
  this.props.history.push("/");
}

handleUnsuccessfulAuth() {
  this.props.handleUnsuccessfulLogin();
}

So now that we have these handlers, all we have to do is call this from the login component, and it will automatically communicate back up to the app component.

So lastly, we need to go to Login, and with Login, we need to pass in the data that we want it to have, which in this case is gonna be handleSuccessfulAuth equals and then this.handleSuccessfulAuth, and then once more, as you may have already guessed, handleUnsuccessfulAuth equals and add in the handler of this.handleUnsuccessfulAuth, and that is all we need to do in the auth component.

<div className="right-column">
  <Login
    handleSuccessfulAuth={this.handleSuccessfulAuth}
    handleUnsuccessfulAuth={this.handleUnsuccessfulAuth}
  />
</div>

Hopefully, this is starting to make a little bit more sense, all we're really talking about is data flow. We're making it possible to update certain values or communicate with functions from one component to another, and so this is the reason why I wanted to take this step-by-step approach as we're doing here.

Then lastly, in the login component, now what we need to do because it already accepts props, so we have everything set up properly. Now what we need to do is inside of the handle submit, right here, instead of just console logging this, now we need to update the prop, we need to call that prop that got passed in.

So in this case, let's go here, we have handleSuccessfulAuth, so right here what I can do is say this.props.handleSuccessfulAuth, and that is all that needs to happen there. I'll get rid of the console log statement, and then I'm gonna do the same thing except on the unsuccessful side, so handleUnsuccessfulAuth, and then the same thing if an error occurs. And like I said, we will focus on these in a later guide, for right now, let's just see if this is working.

login.js

.then(response => {
  if (response.data.status === "created") {
    this.props.handleSuccessfulAuth();
  } else {
    this.setState({
      errorText: "Wrong email or password"
    });
    this.props.handleUnsuccessfulAuth();
  }
})
.catch(error => {
  this.setState({
    errorText: "An error occurred"
  });
  this.props.handleUnsuccessfulAuth();
});

So switching back, I'm once again in the incognito mode because we still do not have that sign-out feature done. I'm gonna refresh just to make sure I have the latest version of the application, go to localhost:3000/auth, and so we're still in this not logged-in state, so let's type in the login that we have with devcamp space, and now if I hit login, there we go, that worked we have been redirected to the homepage, and notice our state in the app is now changed.

large

So this is all working perfectly, so we now have the ability to update the state, not just in our current component that we're in, but even in parent components, and the way we do that is by passing in props and handlers between the components, so this is really cool, I hope that you're as excited about this as me.

I remember the very first time that I built in this kind of functionality into an app, and I was very, very pumped about it because it worked perfectly. It worked exactly as I'd expected it to, and it also made a lot of sense with how I was managing data flow. I was able to directly pass and give instructions from one component to another, so if this is still not making perfect sense to you, if it's still a little bit fuzzy, I highly recommend for you to go through this code and really analyze exactly what we're passing in.

Look at the handlers, look at what they're doing, look at how we're passing them from one component to another. See what they're doing and what the end goal is, which is to update the state.

Now, there are still a number of issues that we need to take care of before this feature is completed. One is we need to go through and analyze any issues with unsuccessful login, so we need to make sure that that's all working properly, so we'll do that, and then we have another issue where if you look at this if I hit command + r, so if I completely refresh, which would be kind of like going to the application, it says I'm not logged in.

large

Now, that's a little bit of a bug because we know that the session still exists, we know the cookie is still inside of the browser. So what we need to do is add a way of checking as soon as the user hits the website for the first time, even if it's the first time in a week or however long, we need to send our cookies up to the server and say, "Are we authenticated? "Are we still logged in?" And if we are, then we should start in the logged-in status. So over the course of the next few guides, we'll walk through each one of those steps.

Resources