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'.
Related
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.
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};
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>
I have a component with a simple button handler method. If I don't bind the onClick handler to this in the constructor (or inline on the onClick itself), then I get an error because the context of the handleAdd method is not the instance where my component's state is...
I understand how the bind works, but is there a workaround to avoid having to use bind all over the place with TypeScript + React?
export class TodoList extends React.Component<ITodoListProps, Partial<ITodoListState>> {
constructor(props: ITodoListProps) {
super(props);
this.state = { items: props.items };
this.handleAdd.bind(this);
}
public render() {
return (
<div>
<button onClick={this.handleAdd}>Add</button>
<ul>
{this.state.items.map((todo, i) => {
return <TodoItem key={i} name={todo.name} />
})}
</ul>
</div>
);
}
handleAdd(e: any) {
this.setState({
items: [...this.state.items, { name: "foo" }]
});
}
}
but is there a workaround to avoid having to use bind all over the place with TypeScript + React?
Yes. Use an arrow function:
handleAdd = (e: any) => {
this.setState({
items: [...this.state.items, { name: "foo" }]
});
}
More
A quick video
More docs on arrow
The arrow function works for anonym functions but reduces the extensibility of the class (if you want to extend that class and redefine the function but using super).
To call the function being a member of the class the easy way is use bind.
Is worst use bind than use anonym functions?
I am familiar with Javascript function bind. But I don't understand why in following React.js snippet is this bind again to this. Has is something common with constructor, because this in constructor can have different values depending on usage?
Thank you in advance for replies
class QuotesLibrary extends React.Component {
constructor(props) {
super(props);
this.search = debounce(this.search.bind(this), 300);
}
search(searchTerm) {
this.props.relay.setVariables({searchTerm});
}
render() {
return (
<div className="quotes-library">
<SearchForm searchAction={this.search} />
<div className="quotes-list">
{this.props.library.quotesConnection.edges.map(edge =>
<Quote key={edge.node.id} quote={edge.node} />
)}
</div>
</div>
)
}
}
What this.search.bind(this) does it that it is binding the key this inside to the function to the context of your React Component and what it basically means is that whenever you try to access a property of the React Component, you can access it like this.props since this will then refer to the React Component's context and not the function itself.
The significance of this.search before bind is that it is trying to access the function search which is in the context of the React Component and hence you are only binding it once and not twice.
I hope I was able to explain the situation properly
You shouldn't use Function.bind(this) : you should use arrow function. Arrow functions are bind to the class (so to the component).
class QuotesLibrary extends React.Component {
constructor(props) {
super(props);
this.search = debounce(this.search, 300);
}
search = (searchTerm) => {
this.props.relay.setVariables({searchTerm});
}
render() {
return (
<div className="quotes-library">
<SearchForm searchAction={this.search} />
<div className="quotes-list">
{this.props.library.quotesConnection.edges.map(edge =>
<Quote key={edge.node.id} quote={edge.node} />
)}
</div>
</div>
)
}
}
Here's an example of how the difference works -
As you can see, the first call will log 'undefined' and the second one will log 'Bar', because the 1st call wasn't binded, and calling functions indirectly (as promise results or as callbacks) doesn't keep the reference to this when it runs - bind tells it what its this is referring to.
function debounce(fn, to) {
setTimeout(fn)
}
class Foo {
constructor () {
this.fullName = 'Bar'
}
speak () {
console.log("My name is", this.fullName)
}
test () {
debounce(this.speak, 1000) // undefined
debounce(this.speak.bind(this), 2000) // "Bar"
}
}
let foo = new Foo()
foo.test()
Why are you saying "again"? You only bind it once, not twice.
debounce from _underscores library takes a function and returns another, therefore to get the this context in the search function you need to bind it to the search.
It's the exact same as binding functions normally in the constructor.