Using Arrow function for class properties in React. Not clear

2k Views Asked by At

I came across the arrow function feature being used as Class property in React component. Looking online I found that it makes code more readable and because of the arrow function features we do not have to bind the handlEvents function inside of constructor.

I still have to use the bind method even while using an arrow function for class property as shown in the code below. When i remove the binding in constructor it shows error in console Warning: A component is changing an uncontrolled input of type text to be controlled. and the form errors do not show up as well

class Contact extends Component {
    constructor(props) {
        super(props);

        this.handleBlur = this.handleBlur(this);
    }

    handleBlur = evt => field => {
        this.setState({
        touched: { ...this.state.touched, [field]: true }
    });

   render() {
       return(
          <Form onSubmit={this.handleSubmit}>
            <FormGroup row>
              <Label htmlFor="firstname" md={2}>
                First Name
              </Label>
              <Col md={10}>
                <Input
                  type="text"
                  id="firstname"
                  name="firstname"
                  placeholder="First Name"
                  valid={errors.firstname === ""}
                  invalid={errors.firstname !== ""}
                  value={this.state.firstname}
                  onBlur={event => {
                    this.handleBlur("firstname");
                  }}
                  onChange={this.handleInputChange}
              />
              <FormFeedback>{errors.firstname}</FormFeedback>
            </Col>
          </FormGroup>
       </Form>
   )

}
3

There are 3 best solutions below

0
On BEST ANSWER

You need to change the function a little bit as follow.

class Contact extends Component {
    constructor(props) {
        super(props);

        this.handleBlur = this.handleBlur(this);
    }

    handleBlur = field => () => {
        this.setState({
        touched: { ...this.state.touched, [field]: true }
    });

   render() {
       return(
          <Form onSubmit={this.handleSubmit}>
            <FormGroup row>
              <Label htmlFor="firstname" md={2}>
                First Name
              </Label>
              <Col md={10}>
                <Input
                  type="text"
                  id="firstname"
                  name="firstname"
                  placeholder="First Name"
                  valid={errors.firstname === ""}
                  invalid={errors.firstname !== ""}
                  value={this.state.firstname}
                  onBlur={this.handleBlur("firstname")}
                  onChange={this.handleInputChange}
              />
              <FormFeedback>{errors.firstname}</FormFeedback>
            </Col>
          </FormGroup>
       </Form>
   )

}
2
On

Arrow functions for early bindings in classes are not officially supported by the current ECMAScript.

Using arrow functions as class methods will get you in trouble when your class is inherited and the child wants to override a parent method.

However, I would say it is pretty safe to use them in your react components as you will not get into trouble with inheritance here, since with react you usually will not further inherit from your own components (see Composition vs Inheritance):

At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies.

Dan Abramov is using arrow functions in component methods as well, however he recommends only to use it if early binding is required.

While it’s still experimental, in my experience it solves the problem fairly nicely. It’s not at all React-specific: I find it useful in any classes that deal with asynchrony and callbacks because the binding problem is common for all JavaScript, not just React. We enabled this syntax proposal in the whole Facebook codebase, and if it gets dropped or changes, we’ll make sure to release an automated codemod to migrate to the new syntax (or, worst case, transform it back into bind calls in constructor).

However as Dan notes, to be on the safe site, stick to early binding in constructors:

If you want to stick to the language standard, manual binding in constructor is the way to go. It’s tedious but usually you only want to do this for event handlers, and by convention you start them with handle* in React, so it’s not too hard to remember to bind those.


Update: regarding your case:

In your case you can either use the solution provided by Anshul Bansal where you pass the fieldname into your handleBlur and make use of the field variable in your closure when you pass the returned function as event callback.

Or you can directly acces the input name of the field via the evt.target (code not tested).

handleBlur = evt => {
    const field = evt.target.name;
    this.setState({
    touched: { ...this.state.touched, [field]: true }
});
0
On

I would not do it with an arrow function, but you can. I will explain the two methods (there are a few more), the first is the one that I use normally.

Binding with a higher order function (or method)

It is simply a method that returns the event callback, as this is a method is already bound to this. This way you can pass any arguments to the method that is a closure, and these arguments will be present in the callback. That is the case of the field argument. Note that I switched the order of the argument, field should be first because it is called first to return the callback.

  handleBlur(field) {
    return evt => {
      console.log(this.state);
      this.setState({
        touched: { ...this.state.touched,
          [field]: true
        }
      });     
    };
  }

And you can bind it simply with:

onBlur = {this.handleBlur("firstname")}

This has the advantage that you do not need to bind to this in the constructor.

Using an arrow function

The code is similar, but you have to bind to this in the constructor.

handleBlurArrow = field => evt => {
      console.log(this.state);
      this.setState({
        touched: { ...this.state.touched,
          [field]: true
        }
      });     
 };

Binding:

 onBlur = {this.handleBlurArrow("firstnameArrow")}

Bind this on constructor:

 this.handleBlurArrow = this.handleBlurArrow.bind(this);

Working example

class Contact extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.handleBlurArrow = this.handleBlurArrow.bind(this);
  }
  
  handleBlurArrow = field => evt => {
      console.log(this.state);
      this.setState({
        touched: { ...this.state.touched,
          [field]: true
        }
      });     
 };
  


  handleBlur(field) {
    return evt => {
      console.log(this.state);
      this.setState({
        touched: { ...this.state.touched,
          [field]: true
        }
      });     
    };
  }

  render() {
    return (<div> 
      <input type = "text"   id = "firstname"
        name = "firstname"
        placeholder = "First Name"
      value = {this.state.firstname}
      onBlur = {this.handleBlur("firstname")}
      onChange = {this.handleInputChange}
      /> 
      <input type = "text"   id = "firstnameArrow"
        name = "firstname"
        placeholder = "First Name Arrow"
      value = {this.state.firstname}
      onBlur = {this.handleBlurArrow("firstnameArrow")}
      onChange = {this.handleInputChange}
      /> 
      </div>

    )

  }
}

ReactDOM.render( <Contact /> ,
    document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>