Wen: Xu Chao, author of React Advanced Road

Authorized to publish, reprint please indicate the author and source


###React In-depth Series 5: Event handling

React In-depth series provides in-depth explanations of key concepts, features, and patterns in React to help you understand React and use React flexibly in your projects.

In Web applications, event processing is an important part, which transforms user’s operation behavior into corresponding logic execution or interface update. React provides multiple methods for handling event responses. This article describes the usage, application scenarios, and advantages and disadvantages of each method.

Using anonymous functions

Code first:

Class MyComponent extends React.Component {class MyComponent extends React.Component {render() {
    return (
      <button onClick={()=>{console.log('button clicked');}}>
        Click
      </button>
    );
  }
}
Copy the code

The Button event response function is an anonymous function, which is probably the most common way to handle event responses. The advantage of this approach is that it’s simple and straightforward. Define an anonymous function handling where the event response needs to be handled. The anonymous function in Code 1 uses the arrow function, which we can do without:

Class MyComponent extends React.Component {render() {
    return (
      <button onClick={function(){console.log('button clicked');}}>
        Click
      </button>
    );
  }
}
Copy the code

Although code 2 runs the same as Code 1, it’s rare to see code 2 written this way in real projects. This is because the arrow function solves the problem of the this binding by binding this in the function body to the current object rather than the object on which the function is called at run time. If this. State is needed in the response function, then code 2 will not work. Therefore, the anonymous function defined by the arrow function is usually used directly in the project as the event response.

The disadvantage of using anonymous functions is that when the event response logic is complex, the anonymous function will have a large amount of code, resulting in the render function becomes bloated, and it is not easy to intuitively see the element structure of the final rendering of the component. In addition, each time the Render method is called, an anonymous function object is recreated, incurring additional performance overhead, which increases further down the hierarchy of the component, since changes to any of the upper components may trigger the render method of that component. Of course, in most cases, this performance penalty is negligible.

Using component methods

The code is as follows:

Class MyComponent extends React.Component {constructor(props) {super(props); this.state = {number: 0}; this.handleClick = this.handleClick.bind(this); // Manually bind this}handleClick() {
    this.setState({
      number: ++this.state.number
    });
  }
  
  render() {
    return( <div> <div>{this.state.number}</div> <button onClick={this.handleClick}> Click </button> </div> ); }}Copy the code

The event response function for clicking Button is the component’s method: handleClick. The advantage of this approach is that no new event response function is created each time the Render method is called, with no additional performance penalty. However, to use this approach, bind this manually in the constructor for the method as an event response (handleClick) : HandleClick = this.handleclick = this.handleclick.bind (this), this is due to ES6 syntax, ES6 Class methods do not bind this to the current instance object by default, we need to manually bind this. Isn’t it a little tedious to manually bind this every time? All right, let’s look at the next way.

Use the attribute initialization syntax

Using ES7 property initializers, you can write:

Class MyComponent extends React.Component {constructor(props) {super(props); this.state = {number: 0}; } handleClick = () => { this.setState({ number: ++this.state.number }); }render() {
    return( <div> <div>{this.state.number}</div> <button onClick={this.handleClick}> Click </button> </div> ); }}Copy the code

That way, you don’t have to bind this manually anymore. However, you should be aware that this feature is still experimental and is not supported by default. This feature is supported by default if you Create your App using the official scaffolding Create React App. You can also use Babel’s transform-class-properties plugin to get support for this feature yourself.

Event response function parameter passing problem

Event response functions are passed an Event object by default. What if I want to pass in additional parameters to the response function?

Using the first method is easy, just use the new parameter:

Class MyComponent extends React.Component {constructor(props) {super(props); This. state = {list: [1,2,3,4], current: 1}; } handleClick(item,event) { this.setState({ current: item }); }render() {
    return (
      <ul>
        {this.state.list.map(
          (item)=>(
            <li className={this.state.current === item ? 'current':' '} onClick={(event) => this.handleClick(item, event)}>{item} </li> ) )} </ul> ); }}Copy the code

In the onClick response function, the new parameter item can be used directly inside the method body.

Using the second option, we can defer the binding of this to render, and bind the additional arguments while this is bound:

Class MyComponent extends React.Component {constructor(props) {super(props); This. state = {list: [1,2,3,4], current: 1}; } handleClick(item) { this.setState({ current: item }); }render() {
    return (
      <ul>
        {this.state.list.map(
          (item)=>(
            <li className={this.state.current === item ? 'current':' '} onClick={this.handleClick.bind(this, item)}>{item} </li> ) )} </ul> ); }}Copy the code

Using the third approach, the solution is basically the same as the second:

Class MyComponent extends React.Component {constructor(props) {super(props); This. state = {list: [1,2,3,4], current: 1}; } handleClick = (item) => { this.setState({ current: item }); }render() {
    return (
      <ul>
        {this.state.list.map(
          (item)=>(
            <li className={this.state.current === item ? 'current':' '} onClick={this.handleClick.bind(undefined, item)}>{item} </li> ) )} </ul> ); }}Copy the code

However, this approach is a bit tricky, because while you don’t need to bind this with bind, you still need to bind other parameters with bind.

There is one other thing to note about the event response function. React passes the Event as an argument to the response function, regardless of whether or not you explicitly declare the Event parameter in the response function. The Event parameter is always positioned after other custom parameters. For example, in Codes 6 and 7 handleClick does not declare an Event parameter, but you can still get the Event object by calling Arguments [1].

To summarize, there are three ways to handle events. The first has an additional performance penalty; The second requires manual binding of this, which increases the amount of code; The third one uses ES7 features and is not currently supported by default. It requires the Babel plugin, but is written in the simplest way and does not require manual binding of this. The second and third methods are generally recommended.


The React Way To Progress

Author: Xu Chao

Graduated from Zhejiang University, master degree, senior front-end engineer, long-term work in energy Internet of things company Vision Intelligence. 8 years of software development experience, familiar with big front-end technology, rich experience in Web front-end and mobile terminal development, especially in React technology stack and mobile Hybrid development technology in-depth understanding and practical experience.



In 2019, iKcamp’s original new book Koa and Node.js Development Actual Combat has been sold on JD.com, Tmall, Amazon and Dangdang!