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.
Related
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.
It is a common practice to be bind a user-created method inside a React.Component class.
class App extends React.Component { // simplified example, not actual code
constructor(){
//...
this.logIn=this.logIn.bind(this) // Binding of a method
}
}
Naturally, this is beceause we need to explicitly bind the method to "this class", otherwise we would be referencing with this the window object!
What's however unclear to me, least from the documentation and so on I viewed, if we use in-built life-cycle methods such as render() or componentDidMount(), majority of the code snippets and also the official documentation seem to not explicitly bind to this
class App extends React.Component {
constructor(){
//....
this.componentDidMount = this.componentDidMount.bind(this)
// is there reason why we don't do this ^ ??
}
}
Is there some in-built binding already inside the React.Component we extend?
Or why don't we need to explicitly bind the life-cycle methods (componentDidMount()) like the rest of our created methods (logIn())?
I've made a component with the following:
...
componentDidMount() {
var that = this;
var x = 0;
}
...
render() {
....
<button onClick={this.componentDidMount}>DID MOUNT</button>
....
}
And the results are in -- when the function does initially mount, that is properly bound, but when clicked from the button, it is not.
Which means that the componentDidMount is not already bound, but it is called from the React internals with the proper context so that it doesn't need to be bound.
-- edit
Perhaps also of note: it's worth checking if you use an autobind package, if that binds the lifecycle methods. autobind-decorator in fact does!
Naturally, this is beceause we need to explicitly bind the method to "this class", otherwise we would be referencing with this the window object!
You can also use arrow functions to been able to use this without binding:
sayHello=()=>{
return 'hello';
}
myOtherFunction=()=>{
console.log('I can acces the other function! Say:'+ this.sayHello())
}
And you don't need to bind the life-cycle methods
Edit: As the documentation says in https://reactjs.org/docs/handling-events.html
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
So it is supposed that lifecycle methods are bound by default.
I looked up what does constructor, super and bind does in General JS.
Example code.
import React from 'react';
class Example extends React.Component {
constructor(props){
super(props);
this.state = { mood: confused }
this.doNothing = this.doNothing.bind(this);
}
doNothing(){} //I really do nothing
render(){
<button onClick={this.doNothing}>I do nothing xD</button>
}
}
Here is what I understand for now
State is an object, in order to create an object within a class I need to use constructor
subclass's constructor will override the parent's constructor, I don't know what is in React.Component but I am sure it is important. I think it is also said in React Document:
Class components should always call the base constructor with props.
super will help me do inherit, and super is the replacement of the parent's constructor. If I need to use this in constructor I need to write super and to go further, I need to pass a parament do super(props) to use this.props
bindwill create a new function that is bounded well with the object, making sure when the function is called, it will be direct to the right object because for some reason if I don't bind it, my button will be like <button onClick={undefined.doNothing}>, because of some class rule :/ (the optional paraments can also help me set pre arguments which are interesting but that isn't what bothers me)
Here is what I don't understand
The meaning of arguments I passed, I have seen some example but they didn't really pass any argument. (The props constructor(props) super(props))
The whole bind code looks odd to me, this.doNothing is changed into what? this.this.doNothing? How does it really works, why my button knows where to find it?
I know this is kinda basic but I did try my best looking things up, I will appreciate if anyone can help me out.
State is an object, in order to create an object within a class I need
to use constructor
You can create whatever object inside a class. Just that state is a special one in React: it need to be defined in constructor to be used in React component life-cycle.
subclass's constructor will override the parent's constructor, I don't
know what is in React.Component but I am sure it is important.
constructor, to my understanding, has three jobs: (1) allows access to this.props by super(props), (2) initialises state and (3) binds functions.
Your later part is on the point.
bindwill create a new function that is bounded well with the object,
making sure when the function is called, it will be direct to the
right object because for some reason if I don't bind it, my button
will be like 'button onClick={undefined.doNothing}>, because of some
class rule
this in React component refers to the component itself. Functions provided by React.Component, e.g. render always have this binding to the component, while your own defined functions don't. So <button onClick={this.doNothing}> in render() will not pose any issue, but doNothing() needs to be bound in constructor to get access to this.
The meaning of arguments I passed, I have seen some example but they
didn't really pass any argument. (The props constructor(props)
super(props))
Look up to point 1. If you use super() instead of super(props), this.props will be undefined inside constructor. It is still accessible in other functions.
Here is the original answer to this.
The whole bind code looks odd to me, this.doNothing is changed into
what? this.this.doNothing? How does it really works, why my button
knows where to find it?
Look up to point 4. this.doNothing().bind(this) allows you to access this inside function doNothing(), including reading state, props and calling other functions of the component. Without binding, this will be undefined inside doNothing().
When you define a variable using the this keyword, it belongs to the scope of the React class and hence can you used throughout the scope of the React class.
The meaning of arguments I passed, I have seen some example but they
didn't really pass any argument.
As far as bind is concerned, .bind takes the context as an argument and returns a function, which when executed will refer to the context of the React class. The rest of the arguments that are passed to bind are made available to the function when it is called.
For example when you write
constructor(props){
super(props);
this.doNothing = this.doNothing.bind(this);
}
The function that is returned by bind is assigned to a variable doNothing which is defined in the class scope. If you change it to
constructor(props){
super(props);
this.someNewFunction = this.doNothing.bind(this);
}
you will use it like
render(){
<button onClick={this.someNewFunction}>I do nothing xD</button>
}
You already set props yourself without knowing:
<button onClick={this.doNothing}>
Here onClick will be set to this.doNothing inside of the props passed to the button constructor. To pass props to your Example class one can do:
<Example color="blue" />
Now in the render method for example you can access it as:
<button style = {{ color: this.props.color }} > Some button </button>
Through that your components can be reused at different places, as the parent can change their props to define their behaviour.
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 have an onClick handler function that passes in the click event and index arguments to the function.
However, turns out I have to declare things in opposite order.. as such:
render(){
return(
<button onClick={this.deleteItem.bind(this, index)}Click Me</button>
)
}
deleteItem(index, e){
e.preventDefault();
this.props.dispatch(deleteTodo(index));
}
This works without issue. But why must this and index be switched in deleteItem()? Is this documented somewhere I am unaware of?
this and index are not being switched. What you're seeing is the difference between using bind to bind a this value and calling the function directly.
In short, if you were to call the function directly, like so:
<button onClick={this.deleteItem}Click Me</button>
You would get the arguments in the order in which you declared them in the function. But, as you obviously know, calling the function in this way leaves you with a this variable referring to the context in which the event handler was called, not the actual class you have declared.
What you're doing instead is calling this.deleteItem.bind, which is necessary to ensure that the this variable is properly bound when it is called in the context of the event.
The better way to do this is the way shown in the React documentation on ES6 classes: bind the event handler in the constructor of the class.
For example,
export class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
this.tick = this.tick.bind(this);
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div onClick={this.tick}>
Clicks: {this.state.count}
</div>
);
}
}
By binding the event handler method in the constructor, it is clearer, and you don't have to worry about the context when you use it.
EDIT
Sometimes, you need to pass specific data to your event handler. This is possible using the constructor binding method here. Just do something like this:
constructor(props) {
super(props);
this.myHandler = this.myHandler.bind(this);
}
myHandler(importantValue, event) {
console.log(importantValue); // will log 'pass this value!'
// and you have access to the event object, if you pass it along
// if you don't need the event object, then just leave it out
}
render() {
<div onClick={(event) => { this.myHandler('pass this value!', event); }>
Click Me!
</div>
}
By adding an anonymous function as your event handler in the onClick property for your element, you can do anything you want!
Check the documentation for bind(). bind() fixes/binds the context (this keyword) of the function to the object specfied in the first argument, and the rest of the arguments are prepended to the bound function. That's why index appears before event.