Reactjs: Passing parameter in onClick method without performance loss [duplicate] - javascript

This question already has answers here:
How to avoid bind or inline arrow functions inside render method
(4 answers)
Closed 4 years ago.
I am new to React and I have been told that when passing methods to the onClick handler you should not:
use inline arrow functions
call .bind(this, parameter)
As they both will create a new function on every single render, which has implications or performance
In my code I have a parent component sending a method (asideSelected()) as a prop to the child component. In the child component, I want to call this method with a parameter that my parent component receives. I have created the following solution:
Parent:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
selected: ""
};
this.asideSelected = this.asideSelected.bind(this);
}
asideSelected(selected) {
this.setState({
selected: selected
});
}
render() {
return (
<Aside
selected={this.asideSelected}
/>
);
}
Child:
export default class Aside extends Component {
constructor(props) {
super(props);
this.selected = this.props.selected.bind(this);
this.showSelected = this.showSelected.bind(this);
}
showSelected(e) {
let selected = e.target.className;
this.selected(selected);
}
<div className="profile" onClick={this.showSelected} src={chat}></div>
}
This solution appears to be working, however, so does using inline arrow functions and binding inside of the onClick, I've never seen "bad" re-render and so I don't know if this is actually any different from the other ways of doing it. If it is unclear what I am attempting to do I am using the events target to pass as a parameter instead of doing it directly inside onClick. I am worried that this is a clunky or sub-par solution.
Any input welcome,
Thank you

The render is triggered by the setState().
Any time you update the state: this.setState(), the component and its children will re-render, you may read the doc here

It is unusual and unnecessary to bind to functions in the constructor of a react class. When you add a new function in the react object, for instance asideSelected(){ ... } in your example above, this function is bound to the prototype of the react object.
By adding this.asideSelected = this.asideSelected.bind(this); in your constructer, you create a new instance of asideSelected directly to the object. By doing this you add 2x the same functions.
Regarding the arrow functions, that is just ES6 syntax that auto scopes you code to this without the need to use .bind(this). This is just syntactical ES6 magic and should in theory not have any performance hit and makes your code look nicer.

Binding should be done in constructor and reason for it given in following link.I am not writing all blog here, because it's little bit lengthy. Let me know, if you don't understand it, then I will try to give more explanation.
Link: https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56

Related

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.

ReactJS bind(this)

I'm following a tutorial to update states in ReactJS. I came across this line in the tutorial this.updateLanguage = this.updateLanguage.bind(this) and I don't understand what is going on. I understand the basics of this and bind, but I've never seen it done like this before. Can someone please explain? The full code:
var React = require('react');
class Popular extends React.Component {
// constructor to set default state
constructor (props) {
super(props);
this.state = {
selectLanguage: 'All'
};
// I don't understand this line
this.updateLanguage = this.updateLanguage.bind(this);
}
// updates state
updateLanguage(lang) {
this.setState(() => {
return {
selectLanguage: lang
}
});
}
render() {
var languages = ['All', 'JavaScript', 'Ruby', 'Java', 'CSS', 'Python'];
return (
<ul className='languages'>
{languages.map((lang) => {
return (
// adds listener to current state
<li style={lang === this.state.selectLanguage ? {color: '#d0021b'}: null}
onClick={this.updateLanguage.bind(null, lang)}
key={lang}>
{lang}
</li>
)
})}
</ul>
)
}
}
module.exports = Popular;
DOM callbacks such as click events will set the this context to the DOM element itself, in this case the li. Try removing the part you don't understand and see what happens - you'll see something like this.setState is not defined, because that function isn't defined in the context of the li element (it's basically looking for li.setState).
What bind is doing on that line is ensuring that whenever that function gets called, it will get called with the this context we want, in this case the Popular component - e.g. Popular.setState.
These days it's getting more and more common to see folks just using fat arrow syntax as a shorthand to preserve the this context - e.g. in this case onClick={ () => this.updateLanguage(lang) }.
(note for those concerned about performance: the fat arrow approach is cleaner but somewhat controversial since it's repeatedly declaring the function on every single render. That said, some folks claim there is minimal or no significant performance impact.)
If you not getting the bind(this) in constructor then its not but one of the way to avoid binding in render
Now why we need bind(this) in render let say we generally use
onChange={this.handleChange.bind(this)}
These approaches assume that you’re declaring React components using ES6 classes. If you use an ES6 class, React no longer autobinds.
So, this is the One way to resolve this is to call bind in render.

Why do event handlers not get the right handle to this? [duplicate]

This question already has answers here:
Unable to access React instance (this) inside event handler [duplicate]
(19 answers)
Closed 5 years ago.
I understand the concept of binding event handlers in order for them to get the proper reference to this. My question is, why does referencing a class's method not provide it the proper this?
Do ES6 classes not automatically provide this on class methods?
class MyComponent extends Component {
clickHandler(event) {
console.log('this', this);
}
render() {
return <button onClick={this.clickHandler}>Button</button>;
}
}
As expected, the above code will print null for the value of this. However, explicitly calling the handler will give the correct value, i.e. <button onClick={event => this.clickHandler(event)} />. Why does the handler have the correct reference to this using the arrow function?
I can hypothesize a bunch of answers, but I'd like the correct one.
Why do event handlers not get the right handle to this?
They do! They get a reference to the element you hooked the event on, as normal.
Do ES6 classes not automatically provide this on class methods?
They don't, no; ES2015 ("ES6") classes use the same fundamental prototypical inheritance and this assignment as ES5 and before, with different syntax (and a couple of new features).
However, explicitly calling the handler will give the correct value
Right, for the same reason that in this code, example is called with this referring to x:
x.example();
...but in this code, example is called with this being undefined (in strict mode; it would be the global object in loose mode):
let e = x.example;
e();
Using class doesn't change the need for this management.
To make this to refer to the component class, you need to either bind this to it, or use an arrow function that closes over the this to use:
Binding (a fairly common pattern):
class MyComponent extends Component {
constructor(...args) { // ***
super(...args); // ***
this.clickHandler = this.clickHandler.bind(this); // ***
} // ***
clickHandler(event) {
console.log('this', this);
}
render() {
return <button onClick={this.clickHandler}>Button</button>;
}
}
Using an arrow function instead (also fairly common):
class MyComponent extends Component {
clickHandler = event => { // ***
console.log('this', this);
}; // ***
render() {
return <button onClick={this.clickHandler}>Button</button>;
}
}
That latter option assumes you're transpiling with support for the upcoming public class fields, which aren't standard yet. (The proposal is still at stage 2 as of this writing.) Instead of creating a property on the prototype, it creates an instance field with an arrow function, which closes over this (which is a reference to the component instance).

React Native: this.setState is not a function

I see a number of questions on here relating to this same issue, but it seems none match the issue I'm having, and are a bit more complex.
I am in the process of learning ReactJS and React Native. I'm in the midst of reading and following the code examples from "Learning React Native" book here: https://github.com/bonniee/learning-react-native
For some reason, calling this.setState in the code below when the handleTextChange function is called, causes the "this.SetState is not a function." error. My question is why? Unlike other questions about this same issue, I don't believe my call to this.stateState is buried in a callback function or if statement. Why is it undefined?
Here is my code:
class WeatherProject extends Component {
constructor(props) {
super(props);
this.state = {
zip: "",
forecast: null
};
}
_handleTextChange(event) {
this.setState({zip: event.nativeEvent.text});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
You input {this.state.zip}.
</Text>
<TextInput
style={styles.input}
onSubmitEditing={this._handleTextChange}/>
</View>
);
}
}
Do not use bind inside a render. bind is a rather expensive operation and should only happen once. you have two options:
either bind the function in the constructor:
this._handleTextChange = this._handleTextChange.bind(this);
or use arrow function:
onSubmitEditing={(e) => this._handleTextChange(e)} />
Edit
Apparently arrow functions inside render is also a bad practice (Thx to Adam Terlson in the comments and answer below). You can read eslint docs which states:
A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary.
Using arrow functions is obviously not as bad as using bind, but nevertheless should be avoided.
Regarding arrow function you also need to change _handleTextChange(event) function. Other answers didn't talk about how to change normal function to arrow function.
You need to change handler function, from:
_handleTextChange(event) {
this.setState({zip: event.nativeEvent.text});
}
To:
_handleTextChange = event => {
this.setState({zip: event.nativeEvent.text});
}
The issue is context binding, as identified in the other comments and answers here.
However, the performance of bind itself is a non-issue. The way-more-relevant issue is that using bind or arrows in your render methods creates a new function on each render, resulting in a change of props for the child that receives them, forcing a re-render.
You have two viable options:
class WeatherProject extends Component {
constructor(props) {
super(props);
this._handleTextChange = this._handleTextChange.bind(this);
}
// ...
}
Or you can use the class property notation and assign arrow functions if you're using the babel plugin for it.
class WeatherProject extends Component {
constructor(props) {
super(props);
// ...
}
handleTextChange = (event) => {
this.setState({zip: event.nativeEvent.text});
}
// ...
}
I strongly recommend you use the eslint package with the react recommended rules enabled. It will catch errors like using bind/arrows in your render, as well as tell you that underscore prefixed functions are ugly and totally not necessary in React. :)
this.setState is not a function--- Solved using this
let that = this;
that.setState({membersArray:response.data})
I had the same problem when trying to set the state. when I bind the function inside the constructor, the issue gets solved. check with below binding
constructor(props) {
super(props);
this.state = {
zip: "",
forecast: null
};
this._handleTextChange = this._handleTextChange.bind(this);
}

Runtime context (this) of the function comes from this.props

We always use like this:
var MainComponent = React.createClass({
changeHandler() {
this.setState({
foo: 'bar';
})
},
render() {
return (<SubComponent onChange={this.changeHandler} />)
}
})
var SubComponent = React.createClass({
render() {
return (<button onClick={this.props.onChange}>Change</button>)
}
})
when we call this.props.onChange in SubComponent, we actually call changeHandler of MainComponent.
I suddenly realize that the context of changeHandler has been changed when pass to SubComponent using this.props, the this inside changeHandler should have been changed to this.props.
But as we all know, when we call changeHandler inside Subcomponent, the this in changeHandler is still MainComponent.
So I guess there may be some 'binding' when when pass function from MainComponent to SubComponent using this.props. The context of changeHandler is bound to MainComponent.
I add an console.log(this.props) in SubComponent's render, and I find some properties been add to this.props.onChange:
__reactBoundArguments: null
__reactBoundContext: point to Constructor MainComponent
__reactBoundMethod: point to function changeHandler
I think these are about the 'binding', but I don't know how they actually work.
Could someone please tell me something detail about this context binding, it's really nag me a lot. How does React do that?
When you define your components with React.createClass() there's a bit of auto-binding behind the scenes.
Your onChange={this.changeHander} effectively becomes onChange={this.changeHandler.bind(this)}. The reason for this is because it's generally what you want. This is documented here:
https://facebook.github.io/react/blog/2013/07/02/react-v0-4-autobind-by-default.html
Note however that if you define your components using the ES6 class syntax, this auto-binding does not occur. This was a deliberate decision to keep the behaviour the same as typical JavaScript classes. This is documented here:
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html
Autobinding
React.createClass has a built-in magic feature that bound all methods to this automatically for you. This can be a little confusing for JavaScript developers that are not used to this feature in other classes, or it can be confusing when they move from React to other classes.
Therefore we decided not to have this built-in into React's class model. You can still explicitly prebind methods in your constructor if you want.

Categories