I have a question.. please let me know..
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
}
handleClick = () => {
console.log("clicking")
};
render() {
return (
<div>
<ul>
{this.state.letters.map((letter) => (
<li onClick={() => this.handleClick()}> // when clicking li, it works.
<li onClick={() => handleClick()}> // when clicking li, it does not works why????
hello
</li>
))}
</ul>
</div>
);
}
}
export default App;
This is very simple code.
My question is why we do write like this.handleClick ??
In my thinking,
When clicking li tag.
this.handleClick function is in arrow function!!
so, arrow function binds this automatically
(in this case, this is bound to App component)
therefore, in arrow function, just handleClick can be found, (// in my thinking)
Because this.handleClick can be found!
But, just writing handleClick is not worked.. ? why ??
// handleClick does not found error...
Can you explain why this happen?
Additionally..
if I changed code like <li onClick={() => console.log(this)} />
We can see this is App component.
Also in App component, we can see handleClick function...
So, this is bounded to App Component,
and then I think in arrow function, handleClick() function can be found. but does not find... why??
You appear to be confusing what it means for arrow functions to, in your words "bind this automatically". Technically, that's not what's happening. See the MDN article:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
They don't automatically bind this, they simply don't create a new this binding (in contrast with functions, which inherently create a new this binding). While you could think of it is "automatically" binding this, that's not really what is going on. A key point though is that it only affects what this means within the scope of the arrow function. It has no bearing on what this it outside of the function. So the fact that you defined handledClick as an arrow function has no bearing on whether you can use this to access it or not. It only affects what this refers to within the function itself.
All of that explains why calling this.handleClick would be expected to work in your render, but trying to call handleClick would not. There is no function handleClick in scope of render. There is a this (the instance of your component), which does happen to have a handleClick method.
Using arrow functions in this context is useful because you don't have to re-bind it. If your handleClick function needed to access this.state or this.props, it would be able to. If you defined it as a standard function, you would need to re-bind it, so that the correct this would be available when it is called.
As others have pointed out though, your code as-is probably doesn't work either way not. You should drop the arrow function wrapping them. As is, you are simply calling a function that returns the handleClick function, not calling it.
Related
I would like to learn how is the value of 'this' set when a function called in JSX as a callback to an eventHandler. I noticed that when I call it directly there is no issue accessing state without getting the famous undefined 'this' error, like so:
import React from "react";
class Accordion extends React.Component {
state = { term: "random term" };
onTitleClick() {
console.log("Title is clicked");
console.log(this.state.term);
}
render() {
const renderedItems = this.props.items.map((item) => {
return (
<React.Fragment key={item.title}>
<div className="title active" onClick={this.onTitleClick()}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
<p>{item.content}</p>
</div>
</React.Fragment>
);
});
return <div className="ui styled accordion">{renderedItems}</div>;
}
}
export default Accordion;
When you pass it as just a reference, the famous 'this' is undefined error comes back. Then we know how to bind the 'this' and so on. I feel like I just memorized the solution and now would like to learn the difference.
onClick={this.onTitleClick()} - This is not how you set the event listener. You just need to pass the name of the function instead of calling it yourself.
As far as your question regarding the value of this is concerned, value is set depending on how the function is called. This is not specific to React, this is just how value of this is set in Javascript.
I noticed that when I call it directly there is no issue accessing
state without getting the famous undefined 'this' error
That's because when you call it like this: this.onTitleClick() - onTitleClick() is called on this which refers to the Accordion component. But as mentioned at the start of this answer, this is not how you set the event listener. Instead of calling this method yourself, you need to let javasctipt call it.
When you pass it as just a reference, the famous 'this' is undefined
error comes back
This is the correct way to add an event listener but you get an error because when javascript calls the event handler function, value of this is not your component, i.e. Accordion.
To solve this issue, you have two options:
Explicitly set this using .bind()
this.onTitleClick = this.onTitleClick.bind(this);
Use arrow functions instead of regular functions as event handlers
onTitleClick = () => {
console.log("Title is clicked");
console.log(this.state.term);
}
Following are couple of related questions that might help in understanding this further:
“this” is undefined inside an anonymous function returned by another function React
How does React share methods between classes(components)
When you pass it as a reference, the this is undefined. In order for this to work, you need to bind the function to the class. You can do that in the constructor
constructor(props) {
this.state = { term: "random term" };
this.onTitleClicked = this.onTitleClicked.bind(this);
}
Also, when you pass it to the component, don't call the function, just pass it
<div className="title active" onClick={this.onTitleClick}>
Notice missing parenthesis by the this.onTitleClick call.
I'm having a real hard time grasping this keyword and how it works with regards to react. I know the correct answer to my problem, but I don't understand WHY it works and my other solutions do not. this.showTime.bind(this) is telling the showTime method to use the Contact object as this in the showTime method. (unless this is part of my misunderstanding).
Here's is my code with a few diff examples.
import React from 'react';
import Users from './Users.js';
class Contact extends React.Component {
constructor() {
super();
this.state = {};
this.showTime = this.showTime.bind(this); // correct solution
}
showTime() {
console.log(this.state);
console.log('tom');
};
render(){
const { name, email, phone } = this.props;
return (
<div>
{console.log(this)}
<h4> onClick ={this.showTime} className="click">click me</h4>
// WORKS BUT THIS.STATE IS UNDEFINED
<h4> onClick ={this.showTime()} className="click">click me</h4>
// WORKS
<h4> onClick ={contact.prototype.showTime} className="click">click me</h4>
// WORKS BUT THIS.STATE IS UNDEFINED
<h4> onClick ={this.showTime.bind(Contact)} className="click">click
me</h4>
// WORKS BUT THIS.STATE IS UNDEFINED
<h4>email: {email}</h4>
<h4>phone: {phone}</h4>
</div>
);
}
}
export default Contact;
Why does invoking the function in the handler give me the desired results compared to just this.showTime?
Logging this keyword inside of the render returns the Contact object so Contact.prototype.showTime.bind(Contact) or this.showTime.bind(Contact) should in theory work but they don't.
I know that this keyword has no scope and that it loses context when nested. It's strict when defined in class context so it doesn't return the window object. I just can't seem to understand what I'm missing here.
Looks like you have some prematurely closed h4 tags in your example but I assume those are just typos. Aside from that here are some notes that might help:
<h4 onClick={this.showTime}> - 'this' should be calling and logging state correctly if you have showTime bound in the constructor or instead you can also use an ES6 arrow function to bind it: showTime = () => {...}
<h4> onClick={this.showTime()}> - 'this' is referring to showTime correctly but it's calling it immediately and not onClick. You could use <h4> onClick={() => this.showTime()}> to allow it to be called onClick but writing it this way is unnecessary in this case since no arguments are being passed and you've already bound showTime in the constructor. A benefit of using an arrow function in-line like this is that it's another way to bind the function without having to bind it in the constructor
<h4> onClick={Contact.prototype.showTime}> - I believe this (Contact with a capital C) should correctly refer to where showTime exists in the prototype chain but I think it's then not being referenced in relation to an instance of the Contact component.
<h4> onClick={this.showTime.bind(Contact)} - if you replace Contact with 'this' it should work as expected (note that this is an accepted way of binding but it may cause some performance issues). I'm not sure why calling it as you have it causes showTime to run but shows undefined state. It may have something to do with what React does behind the scenes. When I log 'this' from showTime while calling it the way you have it the console shows the Contact class as bundled by Webpack.
Sorry I don't have more definitive answers on what's going behind the scenes with the 3rd and 4th examples, but unless you're really curious and want to know why they're working that way I wouldn't worry about it too much and just stick to the prescribed ways of binding in React. Here's a good article showing the various methods: https://www.freecodecamp.org/news/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56/
How can we practically prove the point, After Every render react creates new callback arrow function so it is a bad approach. See below code -
class DankButton extends React.Component {
render() {
// Bad Solution: An arrow function!
return <button onClick={() => this.handleClick()}>Click me!</button>
}
handleClick() {
this.logPhrase()
}
logPhrase() {
console.log('such gnawledge')
}
}
Also, how the below Arrow function class property function really works ?
class DankButton extends React.Component {
render() {
return <button onClick={this.handleClick}>Click me!</button>
}
// ES6 class property-arrow function!
handleClick = () => {
this.logPhrase();
}
logPhrase() {
console.log('such gnawledge')
}
}
I'm not sure i understand what you mean exactly by
How can we practically prove the point
As i understand from your question, i assume that you do realize that in the first example above, a new instance of a function is being created.
With that in mind, when you think about it, there are at least 2 issues when you create and pass a new instance of an object or function:
Maybe less important in most cases, you consume more memory on each
render.
More important (in my opinion) you can potentially interrupt the
Reconciliation and Diffing Algorithm of react by passing a new
prop on each render, this will cause a re-render of the child
component, hence performance issues can arise.
Arrow function class property function really works.
Sorry, Don't know how to prove the new instances of function when using bind, but I can do the latter.
console.log this in your arrow function, and compare it to one that is done as a regular function. Do not use bind at any point. The arrow function's this will be the component's context, while the function based one will be either window or undefined.
Is it ok use closures in react, for event handlers?
For example, i have some function and a lot of menu in navigation
and in navigation component i use something like this:
handleMenuClick(path) {
return () => router.goTo(path)
}
...
<MenuItem
handleTouchTap={this.handleMenuClick('/home')}
>
or i should prefer just arrow function?
<MenuItem
handleTouchTap={() => router.goTo('/home')}
>
first variant really make code cleaner, but i'm worried about performance with a large number of such elements
Both should be avoided.
While they'll both work, they both have the same weakness that they'll cause unnecessary renders because the function is being created dynamically, and will thus present as a different object.
Instead of either of those, you want to create your functions in a static way and then pass them in. For something like your MenuItem, it should just get the string for the path and then have the code to do the routing inside. If it needs the router, you should pass that in instead.
The function should then be a pre-bind-ed function (usually in the constructor) and just passed in.
export class MenuItem extends React.Component {
constructor() {
this.handleClick = () => this.props.router.go(this.props.path);
}
render() {
return (
<Button onClick={ this.handleClick }>Go to link</Button>
);
}
}
You can use an arrow function in the constructor. That way it isn't recreated every render function, and thus you avoid unnecessary renders. That pattern works well for single-line simple functions. For more complex functions, you can also create them as a separate function, then bind it in the constructor.
export class MenuItem extends React.Component {
handleClick() {
this.props.router.go(this.props.path);
}
constructor() {
this.handleClick = this.handleClick.bind(this);
}
render() { /* same as above */ }
}
The point of this is that the handler is the same function every time. If it was different (which both methods you describe above would be), then React would do unnecessary re-renders of the object because it would be a different function every time.
Here are two articles which go into more details:
https://ryanfunduk.com/articles/never-bind-in-render/
https://daveceddia.com/avoid-bind-when-passing-props/
when you define a new method inside a react component (Object) as we know functions are object in javascript.
let reactComponent={
addition: function(){ //some task ...},
render: function(){},
componentWillMount : function(){},
}
so, every new method should be bind with in the object using bind, but render() is already defined so we don't do
this.render = this.render.bind(this)
for each new function, except react lifecycle methods are needed to be added and hence, we call the object (constructor function) methods using this.method().
I just started learning React and JavaScript.
While going through the tutorial, I got to this example code of a component, which creates a toggle button:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
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 way I see it, both handleClick and render functions use the class's this object, which is out of their scope (right?).
So why do I only need to bind it into handleClick?
Javascript assigns scope when calling, not when defining. You need to bind() your handleClick() method to the class so that when it's called from the the template it can still access the class' scope via this.
React templates are compiled into javascript functions, so if you didn't bind() your onClick={this.handleClick} handler would be scoped to the template function that called it. In your case, it would refer to the button that was clicked.
If your event handler never referred to this it wouldn't need binding, but since you're calling this.setState() then binding is necessary to keep the handler aware of the class scope.
In any react class, functions like componentWillMount, componentDidMount, render etc are called by react internally while rendering the elements and we never call these methods.
Now since the scope is decided while calling, it's react's job to call/bind these methods with appropriate scope.So we do not need to bother about these functions.
However the other functions like handleClick in above example is a method made by us and react does not know anything about it. Also this method's call is defined by us (i.e. when the button is clicked). So here it is our job to call/bind this method with right scope.
That is why we only bind handleClick and not render function in above example.