-
Notifications
You must be signed in to change notification settings - Fork 7.7k
Add an explanation here why this.handleEvent won't cause the this
t…
#3500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
KennethKinLum
wants to merge
2
commits into
reactjs:main
Choose a base branch
from
KennethKinLum:patch-7
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this what the paragraphs above and below explain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JavaScript has this property when fn is called
then the
this
is bound to the global object inside offn
But when
is called, then inside of
fn
, thethis
is bound toobj
In ReactJS's case, it is
onClick={this.fn}
So although it looks like the second form above, it actually is the same as assigning
this.fn
tog
, and thenonClick={g}
, which is actually the first form above. I am adding the note saying be careful, it is a pitfall that the form looks likeobj.fn()
but it actually is not the case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, isn't that what the previous and next paragraphs explain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's because when I saw the code
{this.handleClick}
, since it is ReactJS specific, I didn't know how it is transpiled to. So I may think in my mind, that it is invoked asthis.handleClick()
, but it is not. It is actually invoked likelet g = this.handleClick
and theng()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's why this section explains how it is JavaScript behavior and not React specific behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that explanation is not so clear as to, if it is
this.handleClick()
, then it is bound correctly. If it isthis.handleClick
, then it is not bound correctly. There is a subtle difference there.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in your mind
onClick={this.handleClick}
would be transpiled toonClick: this.handleClick()
? Wouldn't you also expect that it is immediately called instead of calling it only when the click happened?I understand that this is a hard to explain topic. But I don't think the new paragraph is helping since it introduces new concepts like "the form of
this.handleEvent()
" and "evaluated to be a reference to a function object".Would it maybe help to show how this somewhat looks in terms of actual code?
But this can be just as problematic because e.g. react event handlers aren't called with native events.
Aside: I'm sure Ricky knows how
this
works and how there's a difference betweenthis.handleClick
andthis.handleClick()
. The problem we should be focusing on is how we explain it and where we can avoid confusion.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually worked on some other projects first, and then came back to seeing
onClick={this.handleClick}
.Because it was
onClick={this.handleClick}
, I might have thought at first it might be something that ReactJS transpiles toonclick: () => this.handleClick()
. Somebody told me it is not done by addEventListener but by createElement, but either case, I might have thought ofonclick: () => this.handleClick()
but in fact, it isonclick: handleClick
. So if it isonclick: () => this.handleClick()
then it actually works, but if it isonclick: handleClick
, then it doesn't work.So I just wanted to add the pitfall and reminder that, when we see
this.handleClick
, don't think of it asthis.handleClick()
. It is just something that, if I remember this pitfall, even if I look at the code after some time, I still remember always to bind it firstthis.handleClick.bind(this)
.So I think if I summarize it as three ways: (1)
this.handleClick.bind(this)
(2)() => this.handleClick()
(3)handleClick = () => { ... }
using arror function when it is defined in the class, then the pitfall can be avoided. I think when a potion of the reader seesonClick={this.handleClick}
, whether they know if it isthis.handleClick()
, then it is in fact correctly bound. Or some of them may think, it in fact looks likethis.handleClick()
, so how come it is not bound correctly? So I just wanted to add the explanation there to say, it is in fact the same asonClick={g}
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, so it might make sense to link to the section that explains how JSX is transpiled in case people are wondering. Otherwise I don't see how we can guard against people making unfounded assumptions.
Aside: Transpiling
onClick={this.handleClick}
toonClick: () => this.handleClick
would break memoization. So less transpilation magic is actually beneficial here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, do you suggest adding a short explanation there or adding a link. If the readers see
onclick: this.h
is the same asonclick: g
, and the binding is lost like that, then we have all the readers covered. Those who don't know why the lost binding can go look up other JS blog or tutorial.