React event handler works without bind() - reactjs

I'm working through a react tutorial and the instructor is showing that the event handler in this code won't work, because this() is accessing the outer environment. But I get no error. Can someone explain it to me?
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0,
};
handleIncrement() {
console.log(this.state);
console.log(this.props);
}
render() {
return (
<div>
<button onClick={this.handleIncrement()}>
Increment
</button>
</div>
);
}
}
export default Counter;

The thing is, when your event handler needs to access this from a local scope, and you call the method like this; this.handleClick(), you are telling JavaScript to implement the task of the method IMMEDIATELY it gets there (in your case, immediately it is rendered), which conventionally, doesn't require binding to this.
But when you 'call' (I put it in quotations because the right word should be REFER) a method like this; this.handleClick, you are actually referring to the method (meaning it should be invoked only when the user does something), not invoking it immediately. This either requires binding this.handleClick= this.handleClick.bind(this); or the use of arrow function for your method handleClick = () => {};. It is mostly used for onClick functionalities.
You are not getting that error in your code because you included the parentheses - this.handleIncrement(). If you remove the parentheses and still consoleLog this.state in your handleIncrement, you will definitely get undefined error. But if your handleIncrement is only logging something outside a state, you will not get the error.
If you understand my basic explanation, kindly accept the answer.

Any function called directly from render method will get the container object as this
But when we assign a function to onClick event, we don't want to call that function immediately... so we assign it like this
<button onClick={this.handleIncrement}>
(only the function name without () at the end) ... and this says to call the function when the button is clicked.
But when you click the button the function will not be called from the render method anymore so the this reference will be changed and produce an error.
In your case, you added the () to your this.handleIncrement function invoking it immediately... so it's not causing any problem but it will give you wrong results in almost all cases since it won't get called on click but it will get called with each render.
Since your simple code gets rendered only on button click it's probably correcting the problem. Add a second button and it will give wrong result or the UI will freeze.
The correct way is to remove the () after this.handleIncreament and bind the function inside constructor ... this.handleIncreament = this.handleIncreament.bind(this)

Without bind() method you can use directly arrow function in handleincrement.
check below code
const { Component } = React;
class Counter extends Component {
state = { count: 0 };
handleIncrement=()=> {
const { count } = this.state;
this.setState({ count: count + 1 });
}
render () {
return <label>
<div>Count {this.state.count}</div>
<button onClick={this.handleIncrement}>Increment</button>
</label>;
}
}
ReactDOM.render(<Counter/>, document.querySelector('main'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main/>

I agree with Afzal Hossain.
<button onClick={this.handleIncrement()}>
This line will call handleIncrement function on render. This is not the correct way to add an event.
<button onClick={this.handleIncrement}>
This will be the correct approach to call the function. But since it's a callback, it will have no knowledge of what this is since it's not in the same context.
React Documentation makes it really clear why we should always bind callback functions with this to have the context available in that particular function.
However, if you don't want to bind your function, there are two workarounds mentioned in react documentation
Public Class Fields syntax
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0,
};
handleIncrement = () => {
console.log(this.state);
console.log(this.props);
}
render() {
return (
<div>
<button onClick={this.handleIncrement}>
Increment
</button>
</div>
);
}
}
export default Counter;
ReactDOM.render(<Counter/>, document.querySelector('main'));
Arrow functions
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0,
};
handleIncrement() {
console.log(this.state);
console.log(this.props);
}
render() {
return (
<div>
<button onClick={() => this.handleIncrement()}>
Increment
</button>
</div>
);
}
}
export default Counter;
For more details, refer to this documentation link.

As Afzal Hossain says, you're invoking this.handleIncrement() when the element renders rather than when the button is clicked.
You need to provide the function handle itself to onClick, and bind() it to the correct context when it is constructed, so that this always accesses the instance of Counter within handleIncrement().
Here is a working implementation of the suggestions made in his answer:
const { Component } = React;
class Counter extends Component {
state = { count: 0 };
handleIncrement = this.handleIncrement.bind(this);
handleIncrement () {
const { count } = this.state;
this.setState({ count: count + 1 });
}
render () {
return <label>
<div>Count {this.state.count}</div>
<button onClick={this.handleIncrement}>Increment</button>
</label>;
}
}
ReactDOM.render(<Counter/>, document.querySelector('main'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main/>

Related

How to solve the lost binding issue for event handlers in React?

In React, it looks like for a self-contained component, we are still experiencing the "lost binding issue":
The following React code in CodePen to change a from 123 to 456 doesn't work:
class Foo extends React.Component {
state = { a: 123 };
clickHandler() {
this.setState({ a: 456 });
}
render() {
return (
<div>
<h1>Hello, world! {this.state.a} </h1>
<button onClick={this.clickHandler}>Click Me</button>
</div>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById('root')
);
It can be solved by
making it onClick={this.clickHandler.bind(this)}
making the handler an arrow function
Is there any other way to write the code to handle the lost binding issue or to make the issue go away altogher? Is it possible to do in the constructor or componentDidMount() to bind all the methods to itself, something like (pseudo code) so that the issue of lost binding can be gone as far as the component is concerned?
for (methodName of this.allMethodNames()) {
this[methodName] = this[methodName].bind(this);
}
If you want automatic binding you'll need to rewrite the onClick to something like this:
<button onClick={() => this.clickHandler()}>Click Me</button>
Otherwise you'll have to do it like this:
<button onClick={this.clickHandler.bind(this)}>Click Me</button>
One way around both of those is to use functional components where you could just pass the function with no issues. Here is your component turned into a functional component here: https://codesandbox.io/s/distracted-nobel-46wgf
import React, { useState } from "react";
import ReactDOM from "react-dom";
export default function Foo() {
const [state, setState] = useState({ a: 123 });
const clickHandler = () => {
setState({ ...state, a: 456 });
};
return (
<div>
<h1>Hello, world! {state.a} </h1>
<button onClick={clickHandler}>Click Me</button>
</div>
);
}
ReactDOM.render(<Foo />, document.getElementById("root"));
Your components needs a constructor, that's where binding is supposed to happen in class-based React:
class Foo extends React.Component {
// Set up your constructor and explicitly pass props in
// React will pass props in automatically but this just makes it easier to read
constructor(props) {
// `this` points to `Foo` now that you have a constructor
// it will also make the state obj available to your custom methods
this.state = { a: 123 };
// inside of your constructor, bind your custom methods to `this`
// if you have more than one, bind them all here
this.clickHandler = this.clickHandler.bind(this)
}
clickHandler() {
this.setState({ a: 456 });
}
render() {
return (
<div>
<h1>Hello, world! {this.state.a} </h1>
<button onClick={this.clickHandler}>Click Me</button>
</div>
);
}
}
You can use ES6 arrow functions. They prevent you from having to call .bind(this) for every function.
clickHandler = () => {
this.setState({ a: 456 });
}
<button onClick={this.clickHandler}>Click Me</button>
I believe there's no way to automatically bind every function you have to bind, simply because you are calling another component whenever you use onClick which makes your this won't work anymore (e.g. you have used a button in your example).
However, there are two other ways, you can either bind it in the constructor (similar to what you suggested to bind it in componentDidMount() but that won't work), like this:
constructor( props ){
super( props );
this.clickHandler = this.clickHandler.bind(this);
}
Or you can either change your function to an arrow function if you do not want to use an arrow function in render method like this onClick={()=>clickHandler()}, which is no good because you'll create a new function instance every time render is called :
clickHandler = () => {
this.setState({ a: 456 });
}
<button onClick={this.clickHandler}>Click Me</button>
I personally recommend the second way because you don't have to write one more line for the binding. More importantly, you can pass extra params directly from the clickHandler like this:
clickHandler = (addMore, addEvenMore) => () => {
this.setState({ a: 456 + addMore + addEvenMore });
// set a to 456 + 100 + 200
}
<button onClick={this.clickHandler(100, 200)}>Click Me</button>
With a binding in constructor, you cannot pass parameters, you have to do the binding when you pass the function to button, making the line clumsy, because the handler now describe multiple actions (the onClick action and the bind action) with the existence of the word bind:
<button onClick={this.clickHandler.bind(this, 100, 200)}>Click Me</button>
You can bind in the constructor. However, if you want "auto binding", I'd recommend using arrow functions since they inherit the lexical scope of this -- where this refers to the parent class versus an unbinded callback method that loses its lexical scope and this refers to the window.
In addition, I'd avoid binding this in the callback function because React has to recreate the function for each re-render (which can lead to an ever so slightly slower performance).
That said, you could do something like the example below -- although I don't recommend it, since not all of these methods would need to be binded to this if they weren't used as a callback in the render method (also Object.getOwnPropertyNames is not supported in older browsers):
import React from "react";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = { a: 123 };
Object.getOwnPropertyNames(App.prototype)
.filter(method => !["constructor", "render"].includes(method))
.forEach(method => {
this[method] = this[method].bind(this);
});
}
handleSetClick() {
this.setState({ a: 456 });
}
handleResetClick() {
this.setState({ a: 123 });
}
render() {
return (
<div>
<h1>Hello, world! {this.state.a} </h1>
<button type="button" onClick={this.handleSetClick}>
Click Me
</button>
<br />
<button type="button" onClick={this.handleResetClick}>
Reset
</button>
</div>
);
}
}
export default App;
Working example:
What it looks like to "auto" bind (compiled):
https://pastebin.com/dZE0Hdnn
What it looks like to use arrow functions (compiled):
https://pastebin.com/e0xmh1fn
The main difference is that arrow functions instantiate the class versus being bound as a prototype to the class when bound in the constructor. This mostly affects inheritance, which you can read more about here. That said, React components don't usually extend from plain classes, instead they either extend from React's Component or PureComponent, and you won't be directly calling a Class.prototype, so take the article with a grain of salt (also, the article is very old and the performance numbers are outdated).
On a side note, you also bind using decorators, although support for decorators is hit or miss (since they're still in proposal stage -- or were, I haven't checked in awhile).

How to correctly initialize a function in React?

tell me, please, how to solve the following problem correctly?
I have a certain component, there is a control above, when I click on it, setState is triggered. I need to call the function this.setScrollLeft () in which I set to the selected node (ref) in this case the cleavage position.
Here is my implementation, but I am sure that there is a better solution:
import React from 'react';
import { ScoreCell, getScoreTheme } from 'components/scores';
class LeaderboardPlayerResult extends React.Component {
constructor(props) {
super(props);
this.containerWidth = 198;
this.data = this.props.data;
this.playerResultRef = React.createRef();
}
componentDidMount() {
this.element = this.playerResultRef.current;
this.element.scrollLeft = this.containerWidth;
}
setScrollLeft = () => {
if (this.element) {
this.element.scrollLeft = this.containerWidth;
}
};
playerResult = () => {
if (this.data.playOffHoles) {
return (
this.data.playOffHoles.map((item, index) => {
return (
<div
className="leaderboard__player-result-row-wrapper"
key={index}
>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holeId}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holePar}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell leaderboard__player-result-cell--score">
<ScoreCell
childCss='tee-times-card__score'
theme={getScoreTheme(item.playOffParScore)}
>{item.playOffParScore}</ScoreCell>
</div>
</div>
</div>
);
})
);
}
};
render() {
console.log('LeaderboardPlayerResult render');
this.setScrollLeft();
return (
<div
className="leaderboard__player-result"
ref={this.playerResultRef}
>
{this.playerResult()}
</div>
);
}
}
The best place to put this.setScrollLeft() is inside the componentDidUpdate method.
You are already calling this method (this.setScrollLeft()) inside componentDidMount, what is right. Now, you could put another call into componentDidUpdate and it will work pretty much as it is working by now because componentDidUpdate is called before render.
The final outcome will be the same, however, you are separating the concerns: render only render the components and the other methods deal with your business logic.
If you are not sure about componentDidMount and componentDidUpdate, see these excerpts from the official React.js documentation:
componentDidMount()
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
componentDidUpdate()
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.

React props is passed, but only render() reads props?

I couldn't find a related situation to mines, however my problem I am having a common error of TypeError: Cannot read property 'props' of undefined.
Weird part is, this error is occurring only for the method I defined above render().
Inside of render() I am able to have access without errors though. React dev tools shows I even have access to props.
Code below:
import { Route } from 'react-router-dom'
import AuthService from '../../utils/authentication/AuthService'
import withAuth from '../../utils/authentication/withAuth'
const Auth = new AuthService()
class HomePage extends Component {
handleLogout() {
Auth.logout()
this.props.history.replace('/login')
}
render() {
console.log(this.props.history)
return (
<div>
<div className="App-header">
<h2>Welcome {this.props.user.userId}</h2>
</div>
<p className="App-intro">
<button type="button" className="form-submit" onClick={this.handleLogout}>Logout</button>
</p>
</div>
)
}
}
export default withAuth(HomePage)
Edit: Apologies. I don't want to cause a confusion either, so I will add that I am also using #babel/plugin-proposal-class-propertiesto avoid this binding.
It's because your method handleLogout has it's own context. In order to pass the this value of the class to your method have to do one of two things:
1) Bind it inside the constructor of the class:
constructor(props) {
super(props)
this.handleLogout = this.handleLogout.bind(this)
}
2) You declare your handleLogout method as an arrow function
handleLogout = () => {
console.log(this.props)
}
this isn't bound in non es6 I believe. So you could either bind it with a constructor, or you may be able to get away with an es6 type function
handleLogout = () => {
Auth.logout()
this.props.history.replace('/login')
}
I can't try this, but you could also do a
constructor(props) {
super(props);
// Don't call this.setState() here!
this.handleLogOut= this.handleLogOut.bind(this);
}
You need to use .bind on your click handler.
<button type="button" className="form-submit" onClick={this.handleLogout.bind(this)}>Logout</button>

using mobx with react functional components and without decorators

I'm trying to get MobX to work with functional components in react. I want to do this without having to use decorators. I have set up an app with create-react-app, added MobX and MobX-react as dependencies.
However, I can't seem to get observables working within functional components.
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
const Test = () => {
extendObservable(this, {
button: false
});
const handleB1 = () => {
this.button = false;
}
const handleB2 = () => {
this.button = true;
}
const getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={handleB2}>Button 2</button>
);
};
const getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={handleB1}>Button 1</button>
);
};
return (
<div>
{this.button ? getButton1() : getButton2()}
</div>
)
};
export default observer(Test);
Clicking the button I would expect the component to get rerendered due to the observable being changed, but I get an error:
×
Error: [mobx] Invariant failed: Side effects like changing state are not
allowed at this point. Are you trying to modify state from, for example, the render
function of a React component? Tried to modify: ObservableObject#2.button
I have tried declaring the observable as part of a functional component or before like this:
const buttonState = () => {
extendObservable(this, {
button: false
});
}
but in both cases I could not get the component to rerender or i was not sure if the observable was actually correctly set.
If i write the whole thing as a class like this it works perfectly
import React from 'react';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
class Test extends React.Component {
constructor(props) {
super();
extendObservable(this, {
button: false
});
}
handleB1 = () => {
this.button = false;
}
handleB2 = () => {
this.button = true;
}
getButton2 = () => {
console.log('button2');
return (
<button type="button" onClick={this.handleB2}>Button 2</button>
);
};
getButton1 = () => {
console.log('button1');
return (
<button type="button" onClick={this.handleB1}>Button 1</button>
);
};
render = () => {
return (
<div>
{this.button ? this.getButton1() : this.getButton2()}
</div>
)
}
};
export default observer(Test);
In React, functional components are not persistent. They run from top to bottom, return some JSX, rinse and repeat.
let i = 0;
const FunctionalComp = (props) => {
const foo = props.foo.toUpperCase();
return <span>Rendered {i++} times. {foo}</span>;
}
All this functional component will ever do is synchronously create the value foo and then return the span. When this component's parent re-renders, this component will do the same exact same, but with potentially new values.
It can never do anything else, and that is why it is powerful. That is why we can call it a functional component: Because it only depends on the values provided to it, because it does not cause side effects that would alter the direction of the rest of the application, and because given the same arguments, this function will produce the same result for the rest of eternity.
Predictable = powerful.
Now, a class component holds persistent state. It constructs, initializes its state, methods, and properties, and then renders and returns JSX. The class (object) still exists in memory, so all of the values and methods on it exist too.
The methods of class component are not so predictable.
class Foo {
name = 'tommy';
getUpperName() {
return this.name.toUpperCase();
}
setName(name) {
this.name = name;
}
}
Foo.getUpperName will not produce the same result every time it is ever used with the same arguments (hint: it doesn't accept any arguments and depends on the context around it to determine its result), so other pieces of the application may change Foo.name and, essentially, control Foo.getUpperName's outcome, potentially by accident.
The class can update its own state, causing itself and all children components to re-compute their JSX returns.
In a plain arrow function, after it returns, all that exists is the return value that it produces and the function statement (declaration). Nothing in between.
All this said, the functional component has no this bound to it. (That is a no-no in functional programming.) It will never have state.
So you can not do anything with this inside of a functional component and it can not hold observable values because every time it re-renders it would re-instantiate each of those values.
In your example above, even if this did refer to Test, this is what would happen:
Test would create the observable value button as false.
Test would render the button, which you would then click.
Test would create the observable value button as false.
Test would render the button, which you would then click.
Test would create the observable value button as false.
Test would render the button, which you would then click.
So on and so forth.
In MobX, your observables need to live on a persistent data structure and be passed into your render functions that return UI markup.
const store = observable({
name: 'tommy'
});
const changeName = () => store.name = store.name.split('').reverse().join('');
const Foo = observer((props) => {
return (
<button onClick={changeName}>{store.name}'s button</button>
)
});
This is not a great pattern, as neither Foo nor changeName are pure, this code would work, at least.
You need to do something like so:
const store = () => {
const self = {};
self.actions = {
setName: action((name) => self.name = name);
}
return extendObservable(self, { name: 'tommy' });
}
const App = (props) => {
return <span><Foo store={store} /></span>
}
const Foo = observer((props) => {
return (
<button onClick={props.store.actions.setName}>
{store.name}'s button
</button>
)
})
Again, this is not an ideal implementation, but it would work, and I am at work and have to get back to what they pay me to do. ;)

React passing parameter with arrow function in child component

I have these parent and child component, I want to pass click function to select an item in child component. Yet it seems the function in child component become automatically called instead of waiting until the user click the element. To make it clearer here is my parent and child components
export class ParentView extends Component {
state = {
selectedItem: {}
}
handleClick = (item) => {
alert('you click me');
this.setState({selectedItem: item});
}
render() {
let item = { name: 'Item-1' };
return (
<div>
<ChildItem item={item} handleClick={this.handleClick} />
</div>
);
}
}
export class ChildItem extends Component {
render() {
const {item, handleClick} = this.props;
return (
<div>
<a onClick={handleClick(item)} />
</div>
);
}
}
Those are my components using arrow function to pass handleClick to child component, yet alert always being called at first render without being triggered by user. Any suggestion?
You should pass a function itself to onClick, not a result of the passed function invocation.
If you would like to invoke it with param, you have options:
bind it with item with handleClick.bind(this, item). bind creates a new function will have a predefined first parameter - item
pass new arrow function like () => handleClick(item)
An example below:
export class ChildItem extends Component {
render() {
const { item, handleClick } = this.props;
return (
<div>
<a onClick={() => handleClick(item)} />
</div>
)
}
}
In your code you're invoking a function in onClick declaration, so the result of handleClick execution will be passed to onClick, what is most likely not something you wanted to achieve.
<a onClick={handleClick(item)} />
Update:
as #dhilt wrote, there is a drawback of such approach. Since the newly created arrow function and .bind also creates new function every time the render method of ChildItem is invoked, react will threat the resulted react element as a different, comparing to the previous "cached" result of render method, that means that likely it might lead to some performance problems in the future, there is even a rule regarding this problem for eslint, but you shouldn't just follow this rule because of two points.
1) performance problems should be measured. we don't forbid using Array.prototype.forEach in favor of a regular for because for is the same or "faster".
2) definition of click handlers as class properties leads to increasing of the initializing step of the component instance. Re-render is fast and efficient in react, so sometimes the initial rendering is more important.
Just use what's better for you and likely read articles like this https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578
Accepted answer has a performance hit: ChildItem component will be re-rendered even if data hasn’t changed because each render allocates a new function (it is so because of .bind; same with arrow functions). In this particular case it is very easy to avoid such a problem by getting handler and its argument right from the props on new public class field:
export class ChildItem extends Component {
onClick = () => {
this.props.handleClick(this.props.item);
}
render() {
return (
<div>
<a onClick={this.onClick} />
</div>
);
}
}
ParentView remains untouched.
The ES6 way:
Using arrow functions =>
onClick={() => handleClick(item)}
(#havenchyk's answer is the ES5 way).

Resources