Two Way Binding in React with Inputs and State
Welcome back to the React Course. In the last guide, we talked about state. In this guide, we're going to further talk about state.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

I'm going to close out my terminal. Right now, we have this.state.color. Right now, it is just blue. So let's head over to the app real quick. What we want is for it to change when we type. So let's go ahead and somehow call this function on when we type. I'll show you how we can do that.

We have this input right here that we're returning. What we need to do is say:

card.js

    render() {

        return (
            <div className="card">
                <h1>{this.state.color}</h1>
                <input onChange={this.handleInputChange}/>
            </div>
        )

        return (
            <div className="card">
                <h1>{this.state.color}</h1>
                { Input('Color') }
                { Input('Plural Noun') }
            </div>
        )
    }
}

Now if we just do this, I'll show you what happens. Let's type, and you'll see: Cannot read property set state of undefined.

large

Let's head over to our app and see what we're writing. We're saying this.handleInputChange. We're saying color: 'red'. What's happening here is that we're trying to call this function but it's not working because this javascript class has no idea what state is. It doesn't know what state is, so it doesn't have this function.

large

It doesn't know where we're getting this function which is part of react, the component class, which we're extending. So the way we can change this is by binding our function in our constructor. So we have to say right here:

card.js

  constructor() {
        super()

        this.state = {
            color: 'BLUE'.
            pluralNoun: ''
        }

        this.handleInputChange = this.handleInputChange.bind(this)
    }

So what's going on here is we're taking this function and we're binding it to our instance of our class. So what I mean by instance is each one of these inputs is its own instance of the input component. So in card.js, we have one instance.

You might be like: "okay it doesn't matter. Why would we need to specify? Why would you need to bind it to the instance? There's only one of them". That's just because there's a possibility that we might create another one. Class is merely a blueprint. You've probably heard that a million times or not at all. It's a blueprint telling us how to build something.

Like the blueprint of a house is not the house itself. Whereas the class of a house or a card is not the card itself. It's just saying this is how we build that card and then it's spitting one out. So handling a change belongs to the entire class, not the instance. So by binding it, we can have access to the specific properties.

That's going to take a little bit for you to completely understand. That probably wasn't an explanation that you could fully understand at the moment and that's just because it takes more than a few tries to fully understand the concept of multiple instances of a class. You might be thinking: "Okay, if it's the blueprint then what's the actual piece? Where do we see the actual thing?" That's probably what you're thinking.

So let's move along and let's try and set the state, now when we type it should work. Now let's go to our application and let's type. You'll see it changes to red. We don't want to change to red though we want it to change to the text we're typing in because we want to be able to have it have that text.

large

If I didn't clarify yet, the reason we want to do that is because we need to type some text in here so that automatically maps over onto this component that we haven't yet built. So as you can imagine, as you can kind of see where this is going, is automatic data transfer or something in that manner to a binding. If you've heard of that.

So we want these letters to appear in this H1, so we can easily do that by getting the value out of input and putting it in here. So if I say:

card.js

    handleInputChange() {
        console.log('this is the value')
        this.setState({ color: 'red' })
    }

So now when we type you'll see that it logs 'this is the value' every single time we have a character, but what we want to do is log out the text. We can put a parameter in here that's called event. Now if we put console.log('this is the value', event), that should print something out unless there is an error which there might be. There's no error. It's working fine.

Now if we were to come down to this function and call it like a function like you might have thought we would do. It is going to call on the initial render and it's going to say "hey we can do this", and that's because it is trying to set state over and over again. Getting that infinite loop again. A way you can write this differently, so you can not do that, is put an arrow function in here.

card.js

    render() {

        return (
            <div className="card">
                <h1>{this.state.color}</h1>
                <input onChange={() => this.handleInputChange()}/>
            </div>
        )

So the reason this works, and it took me a while to understand at first why this is working, I don't understand the syntax but literally, an arrow function is exactly like this function up here. So another way to write handleInputChange is an arrow function would be to say this: handleInputChange = (event) => {}. So that's the same exact thing as this.

Now the way that it would look down here is basically to get rid of the name. We just don't have a name on this and we obviously aren't passing event in here. You can kind of see where this is going.

In the same way, I can write this function, if we add something in here like console.log, you might be wondering why don't we have brackets on here. That's just some ES6 syntax. If we get rid of this console.log and put it up here. We no longer need these brackets.

That's more specific on arrow functions. I recommend you go and learn how to use arrow functions and then move on with this guide. If you're really unaware of how these work. Okay so the next thing we need to do is if we write it this way we need to pass the event in like this:

card.js

    render() {

        return (
            <div className="card">
                <h1>{this.state.color}</h1>
                <input onChange={(event) => this.handleInputChange(event)}/>
            </div>
        )

because if we're calling a function we need to provide the parameter. If we're not doing an arrow function and we're just saying this.handleInputChange it's going to get the event because it's part of input now. Okay so we type in here you'll see it still prints it out.

large

The way we can set this color to the new value is, if you've looked, it's almost obvious if you've looked in this. You'll notice that there is an arrow right here, and there is a target. If you look in here: there should be event.target.value.

large

Within here there is a value somewhere. You'll see that the target is the value of input. Basically, that's going to allow us to get the value of the input, so we type in event.target and print that out.

large

Reload the page and type in here, you'll see that we're now getting this input, but there is nothing in this input. So if we go to our code here and we say event.target.value. This will allow us to get the value of the input, and this is going to make it so it basically prints out the value.

I thought we had maybe mapped it to state but we haven't. I'll show you. It won't allow us to type when we do that. If we type right now it's going to print out these values one by one. See.

large

Now as you can see, you've probably already done it at this point. We don't want to set it to red. We wanted to set it to what we're typing. If you take this and you place it in here instead of red and then get rid of this console.log:

card.js

    handleInputChange(event) {
        this.setState({ color: event.target.value })
    }

Then reload the page: you'll see that when we type it's setting it up here.

large

That can be referred to as a binding in programming. At least I know that's how it's referred to in Angular. I haven't done much Angular, but I've done enough to know some of the general concepts. This is to a binding.

Now this works and all, but if we were to not set the state, and if we were to just type in here.

It's just not setting it, but we know that if we were to say input value={this.state.color} so we can have an initial value. Like when you have a form. Like credit card information or an address when you sign back into an application, like best Buy.com or Instagram or something, and there's a field. It's going to have a piece of state. I don't think that's how I would do it. I would use props.

Basically, what I'm trying to say is we might want to value to appear in here that's already up here right. So if we type in here nothing's going to happen, and the reason nothing happens is because it's set the values to this.state.color so we can't change it because color isn't changing.

If you think about how we just changed color, we can kind of change the value of this by just changing the value of color. Technically we're never even changing the value of the input. We're changing the value of state which is then going to change input. So if you go to your application and start typing you'll see it works the same.

I wanted to say one more thing. This is how you implement this. What we want to use this for, like I've said, is to bind to these so you can see now how we use this. We have all these inputs and then as we type they appear up here.

Technically those generate that the Madlib button isn't even necessary because it's automatically going to be mapping over. So a better name for this might be Show Madlib. We will call it Generate Madlib though.

That's how that works. What we'll do in the next guide is: we are going to talk about how we can get a few more of our inputs on screen because we want those inputs on screen.

Let's save our file. Let's open up our terminals using command + j. Then let's type in: git status, git add ., and what I want to say in here is git commit -m "2-way binding with inputs on card". Okay so let's go ahead and hop into the next guide where we will generate a few more inputs and map them over to our state. See you then.

Resources