Passing an arrow function vs passing the function - javascript

Say I have a function:
handleChange = (e) => {
this.setState({ [e.target.id]: e.target.value });
}
What is the difference between the following:
1.
<FormControl value={this.state.password} onChange={this.handleChange} />
<FormControl value={this.state.password} onChange={(e) => this.handleChange(e)} />

The second case an anonymous function is created which executes the handleChange method and and thereby providing it the context.
Everytime the React component renders, a new function is created in the second and not in the first case since the same reference of handleChange method is being provided to the handler.
You might also want to look at how arrow function in render achieve context binding

Assuming your event handler is written like so in your class
handleChange = (e) => {
this.setState({ [e.target.id]: e.target.value });
}
Let us go to the first example that you have mentioned.
<FormControl value={this.state.password} onChange={this.handleChange} />
Over here, for every change you are passing the memory reference of the handleChange function, and to it the event object is being passed.
Going to the second method.
<FormControl value={this.state.password} onChange={(e) => this.handleChange(e)} />
Here you are creating a new anonymous function, which takes the event object as a parameter, every time an event change occurs. This drastically increases garbage collection if you have large list items.Adding an arrow function in this case is redundant as the context is already bound due to the way you wrote you handleChange method initially. As a perf tip, if you are using arrow functions in your classes, use option 1 for event handlers.

Using arrow function in render may cause some performance issues.
I'd suggest you to use arrow function in class property, but you must use stage-2 features.
Here you'll find a nice comparison between the options:
https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56

In the first case you are using handleChange as event handler.
In the second case you are using a new function as event handler, which in turn calls handleChange.
The difference is that there will be two function calls in the second example. Otherwise they are the same.
In other words: there is no need to use the second form, and it can even be disadvantageous for rerendering.

We can bind our event handlers in class constructor:
we can now access to this inside the event handle
class MyClass extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
handleChange(){
//you can now access "this" inside handlechange
}
}
Looks fine. When we add more event handlers to our class, code should look similar to this:
import React, { Component } from 'react'
import { MyInput, MyAnotherInput } from 'myInputs'
class MyComponent extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.handleClick = this.handleClick.bind(this)
this.handleKeyPress = this.handleKeyPress.bind(this)
}
handleChange(e) {
e.preventDefault()
}
handleClick(e) {
e.preventDefault()
}
handleKeyPress(e) {
e.preventDefault()
if (e.nativeEvent.keyCode === 13) {
console.log('This is enter!')
}
}
render() {
return (
<div>
<MyInput
onChange={ this.handleChange }
onClick={ this.handleClick }
onKeyPress={ this.handleKeyPress }
/>
<MyAnotherInput
onChange={ this.handleChange }
onClick={ this.handleClick }
onKeyPress={ this.handleKeyPress }
/>
</div>
)
}
}
This is, what we can do with Babel compiler with es2015 as preset configuration.
Event handlers with arrow functions
As you have probably seen, when we create event handler method, we always need to add this to constructor, to bind this. Quite tiresome. To be honest, there is no sense to create constructor method only for binding your methods. There should be another solution, and there is.
All what you need is to install stage-1 Babel preset and use arrow functions. If you don’t know, how to do this, go to Babel documentation, it’s very good.
In our case instead of binding methods to this we can writ something like this:
render() {
return(<MyInput onChange={ (e) => this.handleOnChange(e) } />)
}
We have created new anonymous function, which automatically bind this,
that’s why we don’t need to use .bind() method. We have still the same
methods in class, and new arrow functions as wrappers in callbacks
properties.
This is still not perfect solution, because we need to update parameters in arrow function wrappers and we create new instances each time when render method is triggered. Arrow functions in React properties are also not great idea.

When handling an event in JavaScript, the this context out of the box can be very confusing, you can read more about it in this excellent writeup.
Back to your question, the first way onChange={this.handleChange} does not guarantee the this context in handleChange() would always be the same component instance, in many cases, this would refer to the FormControl instance that emits the onChange event.
The second way uses arrow syntax, it would guarantee this would always be the React component instance that handles the event.
In short, using arrow syntax for event handling is preferred in React component classes because it guarantees a consistent this context.

Related

Why we have to bind a method in Class Component React

I was reading the documentation and practicing some things in the React documentation until I finally entered the event handling section. but I don't understand why when using method in class component we have to bind the function, can anyone explain it? for examples :
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
The point is to keep correct value of this reference. Check this example:
class Example {
private prop = 1;
echoProp() {
console.log(this?.prop);
}
}
const example = new Example();
example.echoProp();
const echoPropRef = example.echoProp;
echoPropRef();
In the console you will see 1 and then undefined. This is because:
Example:echoProp's this reference is instance of Example class, so it see also prop property.
if you pass Example:echoProp's reference to another variable (const echoPropRef = example.echoProp - there is no ()), then this reference is changed into undefined.
bind function "freezes" this reference, so it will be always the same; in you example it will be reference to Toggle class.
The bind is necessary if you use code like
<button onClick={this.handleClick}>
When handleClick is triggered by the button, this value inside handleClick will be the click event (this is js behaviour for event listener), but the click event does not have a setState method, which belongs to the component itself. So you want to bind the method to the component, so that this value inside handleClick will be the Toggle component which has the setState method
The this is problem is a general problem in JS (not limited to React).
Normally inside a method, this refers to the current object instance.
Inside an event listener (even if its a method), this refers to the event target.
The this in this.setState must refer to the Component instance.
So to change the value of this, binding is done in the constructor.
Another solution would be
onClick={(evt)=>{handleClick(evt)}}
In this case handleClick ceases being an event listener and the actual event listener would be the anonymous function inside onClick.
The simplest alternative is :
handleClick=(evt)=>{
this.setState(...)
}
Arrow functions do not have their own this & hence acquire the current object (component) instance as this.

React TypeScript callback reading undefined when using setState, but it will log the value

I am writing a react application, and for some reason I can not wrap my head around this issue. Below is a snippet, the only thing that should be needed:
onFirstNameChange(event: any){
console.log(event.target.value)
// this.setState({
// firstname: event.target.value
// })
}
The commented out code will not run, it says it can not read properties of undefined. However when I log the events value it does it perfectly. Any ideas on why this is happening? It is an onchange event. It is also deeply nested, however the value does make it back.
React components written in an ES6 class, do not autobind this to the component's methods. There are 2 solutions primarily. You may use choose either:
Either explicitly bind this in constructor
constructor(props) {
super(props);
// rest of code //
this.state = {
firstname: '',
};
// rest of code //
this.onFirstNameChange = this.onFirstNameChange.bind(this);
}
Or use ES6 Arrow Function
onFirstNameChange = (event: any) => {
console.log(event.target.value);
this.setState({
firstname: event.target.value
});
}
As Brian stated, I also believe the error saying that it cannot read property of undefined, is likely saying it cannot read property setState of undefined, because "this" is undefined.
This is most likely caused by providing the onFirstNameChange handler without leveraging a closure, bind, or arrow function to bind the value of this to the "this" value you are expecting.
My guess is your code leveraging the on change handler looks like the following:
<input type="text" value={this.state.value} onChange={this.onFirstNameChange} />
You can refer to the "Handling Events" page (link here) of the React documentation, you will find the following about midway down the article:
You have to be careful about the meaning of this in JSX callbacks. In
JavaScript, class methods are not bound by default. If you forget to
bind this.handleClick and pass it to onClick, this will be undefined
when the function is actually called.
This is not React-specific behavior; it is a part of how functions
work in JavaScript. Generally, if you refer to a method without ()
after it, such as onClick={this.handleClick}, you should bind that
method.
If calling bind annoys you, there are two ways you can get around
this. If you are using the experimental public class fields syntax,
you can use class fields to correctly bind callbacks.
SOLUTIONS: I've included examples of the 3 possible solutions below to bind the value of this, depending on your preference:
Option 1 - Assuming this is a class based component, which I am based on the syntax shown, you can bind the method in the constructor:
constructor(props) {
super(props);
this.onFirstNameChange.bind(this);
}
Option 2 - Update method definition to public class fields syntax with an arrow function to bind "this": (See here)
onFirstNameChange = (event: any) => {
console.log(event.target.value)
this.setState({
firstname: event.target.value
})
};
Option 3 - Update onChange callback with anonymous arrow function to bind this value:
<input type="text" value={this.state.value} onChange={() => this.onFirstNameChange} />
Note on option 3 from React Docs:
The problem with this syntax is that a different callback is created
each time the LoggingButton renders. In most cases, this is fine.
However, if this callback is passed as a prop to lower components,
those components might do an extra re-rendering. We generally
recommend binding in the constructor or using the class fields syntax,
to avoid this sort of performance problem.

(React) Use arrow function to handle onChange

I am pretty new to React. On the internet I often see code where every onChange event (for example from an input field) has its own function and in the constructor there is that bind method called. Couldn't I just use an arrow function for the event to update the state?
Just like
onChange={(e) => this.setState({text: e.target.value})}
Better to create arrow function as method, because if we use like onChange={(e) => this.setState({text: e.target.value})} we create new instances each time when render method is triggered, so it may affect performance
class MyComponent extends Component {
handleChange = (e) => e.preventDefault()
render() {
return (
<>
<MyInput
onChange={ this.handleChange }
/>
</>
)
}
}
Yes, using an arrow function gives you additional benefits.
like :
You don't need to bind it. you can directly use it.
A normal function can not use without binding. To use the normal function first you need to bind that function, but an arrow function you can use directly.

Does event object / argument get passed automatically to React event handlers?

The answer is probably, yes. Because it works, and we use it all day, I know. I am asking to be %100 sure and for future learners of React. I couldn't see it in the official docs of React, except giving an example for passing multiple arguments to event handlers alongside the event object. So for example: As you can see onFormSubmit, although not having an event argument inside the JSX reference, it has access to the event object to do stuff (preventing page refresh on this example) at the execution.
If you write the onFormSubmit as an inline arrow function like onChange handler, you need to pass the event object, then it is not automatic.
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = { term:''}
this.onFormSubmit = this.onFormSubmit.bind(this)
}
onFormSubmit(event) {
event.preventDefault()
console.log(this.state.term)
}
render() {
return (
<div className="ui segment" >
<form onSubmit={this.onFormSubmit} className="ui form" >
<div className="field" >
<label>Search</label>
<input type="text"
value = {this.state.term}
onChange={event => this.setState({ term: event.target.value })} />
</div>
</form>
</div>
)
}
}
export default SearchBar
You have defined the function to your onChange event handlers which calls the submit method passing the necessary arguments implicity.
There is nothing special about event handlers in React. Every function, if defined works this way.
const a = [1, 2, 3];
function print(elem, idx) {
console.log(elem, idx);
}
// here you have defined the print function.
// the elem and idx are passed implicitly to run the function.
a.forEach(print);
or
// now, rather than defining the function here.
// you have used another arrow function.
// then you need to pass the arguments explicitly.
a.forEach((elem, idx) => print(elem, idx));
React approaches the event handling a little bit differently, using Synthetic Events but this is how callback handlers work generally. If you use a function reference there, the event object is the only argument passed to your function. So, if the event object is the only argument you want to get then you don't need to use an arrow function.
If you want to pass other variables alongside with the event object, then you can use an arrow function.
<form onSubmit={e => this.onFormSubmit(e, otherVar)} className="ui form" >
So, your callback gets the event parameter and you pass this to your handler function with your other variables.
onFormSubmit(event, otherVar) {
event.preventDefault()
console.log(otherVar)
}

React child calling parent handler function, does parent have to pass `this` to child? [duplicate]

class SomeClass extends Component{
someEventHandler(event){
}
render(){
return <input onChange={------here------}>
}
}
I see different versions of ------here------ part.
// 1
return <input onChange={this.someEventHandler.bind(this)}>
// 2
return <input onChange={(event) => { this.someEventHandler(event) }>
// 3
return <input onChange={this.someEventHandler}>
How are the versions different? Or is it just a matter of preference?
Thank you all for answers and comments. All are helpful, and I strongly recommend to read this link FIRST if you are confused as me about this.
http://blog.andrewray.me/react-es6-autobinding-and-createclass/
Binding is not something that is specifc to React, but rather how this works in Javascript. Every function / block has its own context, for functions its more specific to how its called. The React team made a decision for this to not be bound on custom methods on the class (aka not the builtin methods like componentDidMount), when adding ES6 support (class syntax).
When you should bind the context depends on the functions purpose, if you need to access props, state or other members on the class, then you would need to bind it.
For your example, each is different and it depends on how your component is set up.
Pre binding to your class
.bind(this) is used to bind the this context to your components function. However, it returns a new function reference each render cycle! If you don't want to bind on each usage of the function (like in a click handler) you can pre-bind the function.
a. in your constructor do the binding. aka
class SomeClass extends Component{
constructor(){
super();
this.someEventHandler = this.someEventHandler.bind(this);
}
someEventHandler(event){
}
....
}
b. make your custom functions on the class fat arrow functions. aka
class SomeClass extends Component{
someEventHandler = (event) => {
}
....
}
Runtime binding to your class
few common ways to do this
a. you can wrap your components handler function with an inline lambda (fat arrow) function.
onChange={ (event) => this.someEventHandler(event) }
this can provide additional functionality like if you need to pass additional data for the click handler <input onChange={(event) => { this.someEventHandler(event, 'username') }>. The same can be done with bind
b. you can use .bind(this) as described above.
onChange={ this.someEventHandler.bind(this) }
with additional params <input onChange={ this.someEventHandler.bind(this, 'username') }>
If you want to avoid creating a new function reference but still need to pass a parameter, its best to abstract that to a child component. You can read more about that here
In your examples
// 1
return <input onChange={this.someEventHandler.bind(this)}>
This is just doing a runtime event handler bind to your class.
// 2
return <input onChange={(event) => this.someEventHandler(event) }>
Another runtime bind to your class.
// 3
return <input onChange={this.someEventHandler}>
You are just passing the function as the callback function to trigger when the click event happens, with no additional parameters. Make sure to prebind it!
To summarize. Its good to think about how to optimize your code, each method has a utility / purpose depending on what you need.
Why bind a React function?
When you define a component using an ES6 class, a common pattern is for an event handler to be a method on the class. In JavaScript, class methods are not bound by default. If you forget to bind this.someEventHandler and pass it to onChange, this will be undefined when the function is actually called.
Generally, if you refer to a method without () after it, such as onChange={this.someEventHandler}, you should bind that method.
There three ways to bind your onChange function to the correct context
First
return <input onChange={this.someEventHandler.bind(this)}>
In this one we make use of bind explicitly to function to make the onChange event available as an argument to the eventHandler. We can also send some other parameter with type of syntax like
return <input onChange={this.someEventHandler.bind(this, state.value)}>
Second
return <input onChange={(event) => { this.someEventHandler(event) }>
This is a ES6 syntax, whereby we can specifythe parameters that we want to pass to the someEventHandler function. This is equivalent to .bind(this) however, It also gives us the flexibility to send other attributes along with the event like
return <input onChange={(event, value) => { this.someEventHandler(event, value) }>
Third
Define the function someEventHandler using Arrow function
someEventHandler = () => {
console.log(this); // now this refers to context of React component
}
An arrow function does not have its own this, the this value of the enclosing execution context is used and hence the above function gets the correct context.
or bind it in constructor like
constructor(props) {
super(props);
this.someEventHandler = this.someEventHandler.bind(this);
}
return <input onChange={this.someEventHandler}>
In this method, event is directly attached to the someEventHandler function. No other parameters can be passed this way

Categories