Public Class Fields with React Components

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 4 months ago

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.

[00:00] 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.

[00:19] 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.

[00:35] 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."

[00:45] 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.

[00:55] 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."

[01:16] 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.

[01:32] 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.

[01:57] 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.

[02:13] 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.

[02:34] 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.

[02:42] 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.

[03:01] 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.

[03:19] 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.

[03:39] 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.

[03:47] 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.

[04:05] 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.

[04:16] 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.

[04:26] 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.

[04:39] 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.

Vamshi
Vamshi
~ 6 years ago

Excellent video. But why in 2017? :D I wish this came out in 2015/2016

Kent C. Dodds
Kent C. Doddsinstructor
~ 6 years ago

Haha! Yeah, it's probably good to note that Public Class Fields are still relatively new. Stage 2 stuff!

David
David
~ 6 years ago

After many puzzlings of "why this way? Dunno, it works" ... thanks for the light bulb moment!

Kent C. Dodds
Kent C. Doddsinstructor
~ 6 years ago

I'm glad it was helpful!

Gary
Gary
~ 6 years ago

Can you tell me is there a way to enable Public Class Fields in standardjs eslint

Kent C. Dodds
Kent C. Doddsinstructor
~ 6 years ago

Sorry, I don't use standard. I have my own eslint config and I use the babel-eslint parser to get linting on upcoming features like this.

Vamshi
Vamshi
~ 6 years ago

Kent, I was trying this out today

import React, { PropTypes, Component } from 'react';
import Loading from 'gssp-common-lib/lib/components/gssp/leafs/loading/loading.component';

class SurveyQuestions extends Component {
  componentWillMount = () => {
    this.props.overrideEmit('surveyquestions');
  }
  render() {
    return (
        <div>
        {this.props.loading ? <Loading /> : null}
          <h2> Hello World </h2>
        </div>
    );
  }
}

export default SurveyQuestions;

This is undefined. Can you please tell me what Im missing here?

Kent C. Dodds
Kent C. Doddsinstructor
~ 6 years ago

Perhaps I should have mentioned this, but you don't want to do this for lifecycle methods that React will call with your instance. You'll want to use public class fields sparingly.

Vamshi
Vamshi
~ 6 years ago

Thank you!

Gary
Gary
~ 6 years ago

Hi Kent, Found a way,i just had to install babel-eslint and add "parser": "babel-eslint" in .eslintrc file , thanks a lot

Enrique
Enrique
~ 6 years ago

Loved the video. I found it doing a search within Egghead, and I'd like to see the rest of the course that this video belongs to. Do you happen to know which course this video is a part of?

Kent C. Dodds
Kent C. Doddsinstructor
~ 6 years ago

Hi Enrique, It's actually not part of a course. This was just a one-off video I made. Sorry about that!

Enrique
Enrique
~ 6 years ago

No worries! I was assuming that every video was part of a course. Glad I searched for it! It's good!