A New Way to Handle Events in React

William Le

“Property Initializer Syntax” sounds more fancy than it really is. In this bite-sized tutorial see how this alternative way of writing event handlers will help eliminate boilerplate in your constructor and also defend against frivolous memory usage in your renders.

In the Facebook docs, you’ll see event handling done like this:

// option 1
class Thing extends React.Component {
  constructor() {
    this.handleSmthng = this.handleSmthng.bind(this)
  }
  render() {
    <input onChange={this.handleSmthng}/>
  }
  handleSmthng(e) {
    // ...
  }
}

ES6 classes won’t automatically give this scope to handleSmthng, and since you’ll usually want to call this.setState or perhaps invoke another method in the component, the “official” convention is to bind all the event handlers all the time in constructor. This works, but can quickly feel like boilerplate code.


// option 2
class Thing extends React.Component {
  render() {
    <button onClick={() => this.handleSmthng('foo')}>
      ADD
    </button>
  }
  handleSmthng(arg1) {
    // ...
  }
}

This pattern seems to be getting popular in React tutorials online. It will pass this context to handleSmthng and it avoids boilerplate code in the constructor. Heck, there’s no state in this component so you don’t even need a constructor! I think the motivations for this approach are right… but there’s a slight performance cost.

Using an arrow function will always create a new reference in JavaScript which, in turn, increases memory usage for your apps. While memory is cheap in JavaScript, renders are costly in React. When you pass arrow functions to child components, your child component will indiscriminately re-render since (as far as it’s concerned) that arrow function is new data. This could mean the difference between getting 60fps or 50fps for large React applications.

"...However if this callback is passed as a prop to lower components, those components might do extra re-rendering." React Docs

tiddly winks

Two Binds One Closure

There’s a much cleaner way to write event handlers that 1) avoids boilerplate and 2) doesn’t cause extra re-renders: property initializer syntax! It’s a fancy name, but the idea is really simple… just use arrow functions to define your event handlers. Like this:

class TodoApp extends React.Component {
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          ADD
        </button>
        <input onChange={this.handleInput}/>
      </div>
    );
  }
  handleClick = () => {
    // "this"
  }
  handleInput = (e) => {
    // "this", "e"
  }
}

You defined two handlers, and it’s looking really nice. No boilerplate. Easy to read. Easy to re-factor… if you want to pass arguments:

class TodoApp extends React.Component {
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          ADD
        </button>
        <input onChange={this.handleInput}/>
        {
          this.state.todos.map((item) => {
            return (
              <li onClick={this.handleRemove(item.id)}>
                {item.text}
              </li>
            );
          });
        }
      </div>
    )
  }

  handleClick = () => {
    // "this"
  }

  handleInput = (e) => {
    // "this", "e"
  }

  handleRemove = (id) => (e) => {
    // "this", "e", "id"
  }
}

Boom! You can pass arguments without using bind in your render methods, or your constructor! Everything looks really spick and span.

つ ◕_◕ ༽つ Function

If you’re not used to seeing arrow functions this probably looks weird. Remember with ES6 that the fat arrow syntax allows omitting curly braces for one-line statements… it’ll implicitly return whatevers on that line! This is how Babel would transpile handleRemove:

handleRemove: function (item) {
  return function (e) {
    // "item" AND "e" 🌈
  }
}

To configure Babel to use Property Initializer Syntax, make sure you've installed the "transform-class-properties" plugin or enable stage-2. If you're using "create-react-app"... it's already there!

There's an ESLint rule that will tell you not to use "bind" or arrow functions in render

Benefits of Using Property Initializer Syntax

My best guess that Facebook hasn’t “officially” endorsed this pattern in their documentation is because stage-2 ES6 hasn’t been finalized yet, so Property Initializers are still considered non-standards. However, the create-react-app generator already enables stage-2 so… it’s very likely Property Initializers will become de-facto for defining event handlers in the near future.

Once you get comfortable with Property Initializers and begin using them to define handler methods, you’ll gain two notable benefits:

  • Less boilerplate to write Not having to write bind statements in the constructor is pretty sweet. Now you just define the method––and that’s it ✨. If you need to pass arguments, just wrap with a single closure and remember to invoke that handler function in render. As an added benefit, if you need refactor your event handler somewhere else, you only got one place to cut-paste from.
  • Lower memory usage Using arrow functions in render is a bad idea because “by design” renders occur at high volume during a component’s lifecycle; allocating a new pointer for every arrow function. Abstaining from arrow functions in render will ensure you’re keeping your component’s memory usage on a diet.

🌵 Checkout this CodePen to see Property Initializer Syntax in action

✖ Clear

🕵 Search Results

🔎 Searching...