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

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};

Related

React: Why do we not need to bind context to arrow functions being used as event listeners?

Looking at the react tutorial it explains that when we set event listeners, we generally have to bind the functions context, like is done here
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>
);
}
}
I understand this, because unless we bind context to the this.handleClick function we have no guarantee of what value this will actually hold when we call this.setState within the handleClick function. However, the tutorial goes on to state that we would not have to bind context to the handleClick function if we replaced
<button onClick={this.handleClick}>
with <button onClick={() => this.handleClick}>
How come the correct this context is automatically bound when we use an arrow function?
You don't need to but arrow functions keep context of this as the object that defined them where traditional function context this as the object that calls them. You can use normal functions as long as they don't call this or keep a reference to this with a variable link const self = this and use self instead of this.
Why is this the case? Because they designed it to be so.

React: Handling Events - parameter visibility

I am following React Doc. In 'Handling Events' section, the below code segment is there.
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')
);
in handleClick() function, how state is available for it to access? Why not this.state?
This is how this.setState works, its an updater callback passed for this.setState(), as per react documentation:
Passing an update function allows you to access the current state
value inside the updater. Since setState calls are batched, this lets
you chain updates and ensure they build on top of each other instead
of conflicting
more information can be found here as well.
Because it's a function parameter. In this code:
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
This part, is an arrow function, which takes previous state of the component, as a parameter:
state => ({
isToggleOn: !state.isToggleOn
})
And returns a new state, which triggers re-render and so on.
Why not this.state?
The rule of thumb: If your next state depends on previous state, you must use this approach to update your state. So you don't run into a race condition with this.setState calls because is asynchronous function.
Hope it helps :)

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'.

SetState won't update with callback?

I'm having hard time understanding why the text won't change in browser or why it won't even console.log the new state. I'm just trying to change the text by clicking on it.
class Komponentti extends React.Component{
constructor(props){
super(props)
this.state = {teksti: "Hello"}
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState = ({teksti: "Mello"}), function(){
console.log(this.state.teksti);
}
}
render(){
return(
<h1 onClick={this.handleClick}>{this.state.teksti}</h1>
)
}
}
You're calling it wrong. Should be:
handleClick() {
this.setState({teksti: "Mello"}), () => {
console.log(this.state.teksti);
}
}
May be you have been confused with es6 fat arrow functions.
In ES6 we can declare the functions using fat arrow notation to pass the lexical this to the function you declare.
Eg:
const example = () => {
// Something interesting
}
But we call that function as example().
But the setState() is an asynchronous function already declared in the React.
We can use it to update the state in the following manner.
handleClick(){
this.setState({teksti: "Mello"}), () => {
console.log(this.state.teksti);
}
}
Below are the way to set state :
this.setState({valuename:"value"});
this.state.varluename = "value";
this.forceUpdate();

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

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>

Categories