Public Class Fields allow you to add instance properties to the class definition with the assignment operator (
=). In this lesson, we'll look at their use case for simplifying event callbacks and state initialization with a React component.
Here we have a basic react component called "App." In its constructor, it is initializing some state. In the render method, we're rendering out the Click Count. That's the current state of the clicks, as well as a button called "Click Me!" that passes the handleClick handler to the onClick callback.
Here in handleClick where you setState and we increment clicks. Here there's a problem if we go ahead and click, then we're going to see an error "this.setState is not a function." That's because we're passing handleClick to this onClick callback.
When react is calling our our onClick callback with the event, it's not calling it with the right context. This is not actually bound to anything at all. That's why we're getting this "this.setState is not a function."
This is pretty easy to solve. Most people will just make this an arrow function. Then you'll have to invoke this. Now if we click, we'll see that the click count is incrementing.
There are a couple of drawbacks to this. First of, if you want to pass on the event, now you have to type "event" here and "event" here so that you can get access to the event. Another issue there is if there are multiple parameters, then you have to say "param2," then you'll have to go here and "param2."
It won't be long before you start just saying, "The heck with it." We'll say "args" and "...args" so that you don't have to keep in adding parameters, or whatever. But this is not really ergonomic. I don't really like doing things this way.
Another way to accomplish this same kind of idea is instead of using arrow function, which doesn't have at this binding and simply uses this binding of its closure, we'll say ".bind this," so we specify that this binding for the function explicitly. This works just as well and you don't need to worry about parameters or any arguments or anything.
That's great, but I still don't really like having to do the binding here. It also suffers from the performance issues with the arrow function where you're creating a new function every single time the render method is called, which is actually a lot of times often in react components.
Instead of doing the binding here, we can actually do the binding in our constructor. We can say "this.handleClick = this.handleClick.bind this." What this does effectively is it overwrites these instances of handleClick with a bound version of handleClick so that you can pass it on.
It doesn't matter how it's being called. It's always going to be bound with this, with our instance of the component. That works great.
Let's say that you have many handleClick or of handle key up or handle mouse over, you have a bunch of these in your component. Now you have to have not only the definition, but also a line in your constructor binding that definition. That's also not super ergonomic.
With public class fields, you can actually solve this problem by basically taking this assignment looking operator and sticking it right here in the class definition. Then we'll need to turn this into a function, and then we want to have this function be bound to this. Then we'll comment this out.
Because we have this assignment syntax here, the public class field specification basically says, "Postpone evaluating this expression until each instance is being constructed." We're going to add a handleClick property on each instance and assign it to this expression.
This runs effectively. It runs in the controller effectively. You're doing the same type of thing as this would do as we had before.
Now we can click and that works just fine. We can make this even a little bit more ergonomic by removing the binding here and just going with lexical binding for this using an arrow function. Now, we can click and increment and everything works nicely with public class fields.
We can do the same things with state. We'll just copy and paste the state assignment and put it as part of the class definition. Now that state assignment will take place when the instance is being constructed as well.
We'll get rid of this, have that commented out. You'll see that the click count is still initialized to zero. If we initialize it to six, then that works just fine.
Now you can see that the constructor is totally useless. It's not really doing anything at all. We can get rid of that entirely and have our App component still function exactly as we want it to.
That's public class fields. It's simply the name of the property, and then the "equals" operator. Then the expression that we want to have it initialize to when each instance is being initialized.