Tutorial

A New Way to Handle Events in React

Updated on April 16, 2020
Default avatar

By William Le

A New Way to Handle Events in React

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

*“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.

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.

Check out this CodePen to see Property Initializer Syntax in action

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
William Le

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel