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