How does bind work inside react classes and JSX events ? [duplicate] - javascript

This question already has answers here:
Why is it necessary to use bind when working with ES6 and ReactJS?
(3 answers)
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 5 years ago.
I'm trying here to understand the necessity of the binding I commented ..
class Toggle extends Component {
constructor(props){
super(props);
//this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this);
}
render() {
return (
<button onClick={this.handleClick}>
...
</button>
);
}
}
when I log this inside the handleClick it outputs null ..
I understand that the binding is needed to connect the handleClick method to the Toggle Object ..
But shouldn't the log now look for where was the handleClick invoked or called so it would output global object ?
Or should it have looked for 'what' invoked the handleClick inside the onClick which is the other this that refer there to the Toggle Object so it would output it ?

If you bind handleClick in Toggle, it will indeed bind the method to the class as shown below, when I tried to log the value property in the state. It will throw an error otherwise and you can check it in your browser console (NOT in the code snippet one).
I'm not 100% sure why console.log(this) in unbinded handleClick (The OP of this post had similar problem) returns undefined, but my guess that it could be that it is referring to a null component, as it has undefined for its this value.
class Toggle extends React.Component {
constructor(props){
super(props);
this.state = { value: 'hi' }
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.state.value);
}
render() {
return (
<div>
<button onClick={this.handleClick}>
Hello
</button>
<NullComponent />
</div>
);
}
}
let NullComponent = () => {
console.log('calling from null component ', this);
return null;
}
ReactDOM.render(<Toggle />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Related

Why my state does not change on click event with setState [duplicate]

This question already has answers here:
setState doesn't update the state immediately [duplicate]
(15 answers)
Closed 3 years ago.
I'm learning React, and actually don't understand why my state does not change immediately when I press a button, with a click event. I mean, I created a state "test: false", but when I click on my button, I just want to change the state, so I used setState. But the console still shows me the "false" when I click for the first time.
Here's a Codesandbox to illustrate my example.
import React from "react";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
test: false
};
this.display.bind(this);
}
display() {
this.setState({
test: true
});
console.log(this.state.test)
}
render() {
return (
<div className="App">
<button onClick={() => this.display()}>Click me!</button>
</div>
);
}
}
export default App;
I also want to show "Hello" if state is true and "Goodbye" if the state is false.
Can anyone help me, please?
I have checked your code and found you are doing mistake there.
As setState() is async. One easy way to handle that is to give it a callback.
To print the state just after the setState(), you need to use the code as follows:
display() {
this.setState({
test: true
}, console.log(this.state.test));
}
and about your updated query, you need to use ternary operator as follows:
render() {
return (
<div className="App">
<button onClick={() => this.display()}>Click me!</button>
{(this.state.test) ? <p>Hello</p> : <p>Bye</p>}
</div>
);
}
make display an asynchronous function and you can also use es6 for your function deceleration not compulsory though
const display = async _ => {
await this.setState({
test: !this.state.test
});
await console.log(this.state.test)
}
//now your button should appear like this
<button onClick={_ => display()}>Click me!</button>

Why should i need the bind when using event in React

could someone explain to me why i need to use binding this.handleClick = this.handleClick.bind(this); when using event?
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(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
And why the value of 'this' is defined?
class Person {
constructor(name, yearOfBirth) {
this.name = name;
this.yearOfBirth = yearOfBirth;
}
calculateAge() {
console.log(this);
}
}
const john = new Person('John', 1993);
john.calculateAge();
But the value of 'this' is undefined when clicked?
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
console.log(this);
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
ReactDOM.render(
<ActionLink />,
document.getElementById('root')
);
this is always executed in the context of where the function was invoked.
So when a React component renders, the elements and the event handlers will be attached to the DOM and will be executed in the DOM's context. So if you don't bind the method to the component, you cannot use this inside the method.
// this will be window and window.setState will be undefinedd
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
When you bind the method to the Component, this will execute in the Component's context and you will be able to invoke .setState.
Interestingly, if your method doesn't use any this invocations, you won't have any problems even if you don't bind.
As quirimmo pointed out in the comments, in strict mode, this will be undefined by default and ES6 Classes are executed in strict mode by default even if not specified explicitely. That's why this will be undefined if not bound to the class.
This is undefined in the snippets you've posted for just that reason, they are undefined.
In the first, you ask why you need to for the function to work- its because you're trying to call the components 'this' to call set state. So to do that you just bind this from the components constructor so that when you call that function 'this' is defined and set to the components 'this'.

React-JS TypeError: Cannot read property 'setState' of undefined [duplicate]

This question already has answers here:
Unable to access React instance (this) inside event handler [duplicate]
(19 answers)
Closed 5 years ago.
I get "TypeError: Cannot read property 'setState' of undefined" if I try to call setState in a function (edit or save).
It works if I declare an arrow function instead, but I do not understand why?
import React, { Component } from 'react';
class Note extends Component {
constructor(props) {
super(props);
this.state = {
editing: false
}
}
edit (){
this.setState({ editing: true })
}
save() {
this.setState({ editing: false })
}
renderEditNote() {
return (
<div className="note">
<textarea></textarea>
<button onClick={this.save}>SAVE</button>
</div>
)
}
renderDisplayNote() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button onClick={this.edit}>Edit</button>
<button onClick={this.remove}>X</button>
</span>
</div>
)
}
render() {
return this.state.editing ? this.renderEditNote() : this.renderDisplayNote()
}
}
export default Note
You need to bind this for edit and save functions so it doesn't get lost when called-back by the onClick.
For example:
<button onClick={this.edit.bind(this)}>Edit</button>
Or you can do it in the constructor as:
this.edit = this.edit.bind(this);
To make it works, you should do
this.save.bind(this)
this.edit.bind(this)
this.remove.bind(this)
You have not bind it yet, so the this in your method edit(), save() and remove() is not the Note class.
You can do console.log(this) in those methods to understand why

Why do we need to Bind 'this' for JSX callbacks [duplicate]

This question already has answers here:
Why is JavaScript bind() necessary?
(4 answers)
Closed 5 years ago.
In this example here, we need to bind the 'this' object of handleClick() to the global scope:
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>
);
}
}
However, handleClick() is defined in the scope of the component, so shouldn't we not need to specify the 'this' object for this function, as it will already refer to the component itself?
You are correct, but you're missing one thing. The scope of this without binding to the handler IS the component. So what happens when you want the context of the event? You don't have it.
Another way to solve this is to lexically bind the component, so that you don't have to worry about manually binding each and every function.
handleClick = () => { //arrow function applied
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
Note that now you don't need a component constructor here now anymore as well.
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);
}
Now becomes
state = {isToggleOn: true};

handlers inside a component invoking eachothers code by mistake

I'm lost! i have a really simple component but its going nuts.
i got an input with a change function and a button with onclick.
for some reason when i set the state in the onclick i get an error
Maximum update depth exceeded. This can happen when a component
repeatedly calls setState inside componentWillUpdate or
componentDidUpdate. React limits the number of nested updates to
prevent infinite loops.
Huh? im not doing any looping whats going on with this error then?
so i comment the line where i set the state and added a console log to see the value. now i discovered a true magic, when i run the code its showing in the console the value from my state without me clicking anything (the only place i use console.log is in the click event).
When i do click the button though, nothing really happens.
but listen to this, when i type inside my input, again the console logs the current value in the state!
why the console.log() call from the wrong method? is this a bug?
Here is my code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
myValue: "Hello World"
};
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleClick = (value) => {
console.log(value);
//this.setState({ myValue: value }); // this will error about infinite loop
};
handleChange = (e) => {
this.setState({ myValue: e.target.value });
}
render() {
return (
<div>
<div>
<input value={this.state.myValue} onChange={this.handleChange} />
</div>
<button onClick={this.handleClick(this.state.myValue)}>set</button>
<h3>{this.state.myValue}</h3>
</div>
);
}
}
render(<App />, document.getElementById("root"));
The problem is that you are passing to the onClick event a function invocation, and not a function reference.
im not doing any looping whats going on with this error then?
In the first initial render call when you pass the handler function, you actually invoke it. This function is updating the state which triggers another render call which you pass again a function invocation that will do another update to the state which will trigger another render call and so on.
Hence an infinite loop.
why the console.log() call from the wrong method? is this a bug?
As i mentioned above, you are passing a function invocation, hence on each render you call console.log(value) instead of listening to the onClick event, and when you change the input (which works as expected) you rerender again and call console.log(value) once more. So it's not the handleChange that calling the console.log, it is render function that is calling handleClick which invoke console.log.
No bugs or magics here, it may not be the desired behavior but it is the expected behavior according to your code and logic.
You got 2 main options here.
Easy and fast fix: Pass a function reference that returns a function with your logic there. I'm using currying, so with ES6
arrow functions is just as easy as adding another parameter and an
arrow:
handleClick = (value) => (e) => {
console.log(value);
this.setState({ myValue: value });
};
This approach does have it's advantages, like fast implementation
and easy to understand and read the code but it may cause
performance issues.
You see, you are returning a new instance of a
function on each render and react will treat it as a new prop this
can interrupt the diffing algorithm of react.
A working example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
myValue: "Hello World"
};
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleClick = (value) => (e) => {
console.log(value);
this.setState({ myValue: value });
};
handleChange = (e) => {
this.setState({ myValue: e.target.value });
}
render() {
return (
<div>
<div>
<input value={this.state.myValue} onChange={this.handleChange} />
</div>
<button onClick={this.handleClick(this.state.myValue)}>set</button>
<h3>{this.state.myValue}</h3>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
A better approach that is considered as best practice, is to compose a
component that accepts an event and a parameter as separate props
and will send this value upwards when the event is triggered.
For example MyButton.js:
class MyButton extends React.Component{
handleClick = e =>{
const {onClick, clickValue} = this.props;
this.props.onClick(clickValue);
}
render(){
const {children} = this.props;
return <button onClick={this.handleClick}>{children}</button>
}
}
A working example:
class MyButton extends React.Component{
handleClick = e =>{
const {onClick, clickValue} = this.props;
this.props.onClick(clickValue);
}
render(){
const {children} = this.props;
return <button onClick={this.handleClick}>{children}</button>
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
myValue: "Hello World"
};
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleClick = (value) => {
console.log(value);
this.setState({ myValue: value });
};
handleChange = (e) => {
this.setState({ myValue: e.target.value });
}
render() {
const {myValue} = this.state;
return (
<div>
<div>
<input value={myValue} onChange={this.handleChange} />
</div>
<MyButton clickValue={myValue} onClick={this.handleClick}>set</MyButton>
<h3>{myValue}</h3>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
By the way, you don't need to bind the handlers to the class when you're using arrow functions. it binds the this automatically.
The body of ES6 arrow functions share the same lexical this as the
code that surrounds them, which gets us the desired result because of
the way that ES7 property initializers are scoped
As mentioned you are passing a function invocation than a function reference, so the simple and recommended change that one needs to do is to give a reference to a thick arrow function (or anonymous function):
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
myValue: "Hello World"
};
}
handleClick = (value) => {
console.log(value);
// No longer gives error
this.setState({ myValue: value });
};
handleChange = (e) => {
this.setState({ myValue: e.target.value });
}
render() {
return (
<div>
<div>
<input value={this.state.myValue} onChange={ () => {this.handleChange();} } />
</div>
<button onClick={ () => {this.handleClick(this.state.myValue);} }>set</button>
<h3>{this.state.myValue}</h3>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Now when the <App /> component is rendered it no longer has the function invocation as the value in onClick or onChange event listeners, instead, it has a reference to the anonymous function which in turn calls your handleClick() and handleChange() functions. Hope it helps! :)

Categories