Something that is puzzling me is why when I define a react component class, values contained in the this object are undefined in methods defined (this is available in lifecycle methods) within the class unless I use .bind(this) or define the method using an arrow function for example in the following code this.state will be undefined in the renderElements function because I did not define it with an arrow function and did not use .bind(this)
class MyComponent extends React.Component {
constructor() {
super();
this.state = { elements: 5 }
}
renderElements() {
const output = [];
// In the following this.state.elements will be undefined
// because I have not used .bind(this) on this method in the constructor
// example: this.renderElements = this.renderElements.bind(this)
for(let i = 0; i < this.state.elements; i ++){
output.push(<div key={i} />);
}
return output;
}
// .this is defined inside of the lifecycle methods and
// therefore do not need call .bind(this) on the render method.
render() {
return (
<div onClick={this.renderElements}></div>
);
}
}
Then in the following example I do not need to use .bind(this) or an arrow function, this is available as expected in speak function
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
speak() {
console.log(this.name + ' barks.');
}
}
var d = new Dog('Mitzie');
d.speak();
http://jsbin.com/cadoduxuye/edit?js,console
To clarify, my question is two part. One) why in the second code example do I not need to call .bind(this) to the speak function, but I do in the React component for the renderElements function and Two) why do the lifecycle methods (render, componentDidMount, etc) already have access to the class' this object, but renderElements does not.
In the React docs it says the following
[React Component Class] Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance.
But clearly they do, as the second code example I've posted shows.
Update
Both links in the first two comments show a working example of React classes NOT using .bind(this) on class methods and it works fine. But still in the docs is explicitly says you need to bind your methods, or use an arrow function. In a project using gulp and babel I can reproduce. Could it mean browsers have updated things?
Update 2
My initial code example had this.renderElements() called directly in the render function. That would work as expected without binding the function, or defining it with an arrow function. The issue occurs when I put the function as an onClick handler.
Update 3
The issue occurs when I put the function as an onClick handler.
In fact it is not an issue at all. The context of this changes when passed to the onClick handler, so that's just how JS works.
The value of this primarily depends on how the function is called. Given d.speak();, this will refer to d because the function is called as an "object method".
But in <div>{this.renderElements}</div> you are not calling the function. You are passing the function to React which will call it somehow. When it is called, React doesn't know which object the function "belonged" to so it cannot set the right value for this. Binding solves that
I actually think what you really want is
<div>{this.renderElements()}</div>
// call function ^^
i.e call the function as an object method. Then you don't have to bind it.
Have a look at MDN to learn more about this.
Event handlers in the component will not be bound automatically to the component instance like other methods ( life cycle methods...).
class MyComponent extends React.Component {
render(){
return (
<div onClick={this.renderElements}>
{this.renderElements()} <-- `this` is still in side the MyComponent context
</div>
)
}
}
//under the hood
var instance = new MyComponent();
var element = instance.render();
//click on div
element.onClick() <-- `this` inside renderElements refers to the window object now
Check this example to understand this more :
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
run(){
console.log(this.name + ' runs');
}
speak() {
console.log(this.name + ' barks.');
this.run(); <-- `this` is still in the Dog context
return {onRun : this.run};
}
}
var d = new Dog('Mitzie');
var myDog = d.speak();
myDog.onRun() <-- `this` is now in the global context which is the `window` object
You can check this article for more information.
Functions in ES6 Classes - the case is explained very well by #Felix Kling. Every time you call a function on an object, this points to the object.
Lifecycle methods in React.Component - whenever React instantiates your component like myComponent = new MyComponent() it knows which object to call the lifecycle methods on, namely myComponent. So a simple call myComponent.componentDidUpdate() makes this available in the componentDidUpdate lifecycle method. Same for the other lifecycle methods.
Handlers & Bound in React.Component - this.state is undefined, because this is actually window - log it and see. The reason is that React invokes handlers on the global context, unless you have the handler bound to another context which overrides window (see #Phi Nguyen's answer also).
I think they have done that to allow you more flexibility, because in complex applications your handler may come from another component passed through props and then you would like to have the possibility to say: "Hey, React - this is not my component, but it's parent."
React's documentation is a bid misleading when it says
Methods follow the same semantics as regular ES6 classes, meaning that
they don't automatically bind this to the instance.
What they mean is that
var dog = new Dog('Mitzie');
speak = d.speak;
dog.speak() // this will be dog, because the function is called on dog
speak() // this will be window, and not dog, because the function is not bound
1.
Arrow Functions:
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous. These function expressions are best suited for non-method functions and they can not be used as constructors.
Function.prototype.bind():
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
2.Component Specs and Lifecycle
To be absolutely clear: Most lifecycle methods are not bound, but called on an instance using the dot notation (true for componentWillMount, componentWillUnmount, componentWillReceiveProps and so on...), except componentDidMount which is bound to the instance since it gets enqueued into the transaction.
Just always put the autoBind(this); code in your constructor and never worry about method pointers.
npm install --save auto-bind-inheritance
const autoBind = require('auto-bind-inheritance');
class Animal {
constructor(name) {
autoBind(this);
this.name = name;
}
printName() { console.log(this.name); }
...
}
let m = new Animal('dog');
let mpntr = m.printName;
m.printName() //> 'dog'
mpntr() //> 'dog', because auto-bind, binds 'this' to the method.
Related
This question already has answers here:
Why and when do we need to bind functions and eventHandlers in React?
(2 answers)
Closed 3 years ago.
look at this code for example
import React, { Component } from ‘react’;
class App extends Component {
constructor(props) {
super(props);
this.clickFunction = this.clickFunction.bind(this);
}
clickFunction() {
console.log(this.props.value);
}
render() {
return(
<div onClick={this.clickFunction}>Click Me!</div>
);
}
}
what's the purpose of bind(this) ? it binds the function clickFunction to the context of the object which clickFunction is already bound to , let me illustrate what i am trying to say with a normal javascript code :
class my_class {
constructor() {
this.run = this.run.bind(this)
}
run() {
console.log(this.data)
}
}
my_class.data = 'this is data'
new my_class().run() //outputs 'undefined'
and if you remove bind(this) it will give you the same results
constructor() {
this.run = this.run
}
results :
new my_class().run() //still outputs 'undefined'
i am sure i am understanding something wrong and this might the worst question on earth however i am new to es6 and i am not used to classes yet so i apologize for that
Blame JavaScript not React. This is done to retain object instance when the function is going to be passed. Certainly, it must be semantically correct for the function to expect such object. Most common case is to bind this when passing object method. The keyword This depends on how the function is called not how/where it is created. The relationship to This should be maintained at invocation.
Consider:
class Welcome extends React.Component {
render() {
return <button onClick={this.sayName}>Say My
Name</button>;
}
sayName() {
alert(this.props.name);
}
}
In React, you invoke like this: . This renders a button. Clicking the button should trigger an alert with "Bob".
Except it doesn't. Because in the above example, this would be undefined in the sayName function.
What's happening inside the render function is that this refers to the current instance of our React component. That component has a sayName function defined, so this.sayName points to our function, just fine and dandy.
But what React is doing behind the scenes is assigning this.sayName to another variable. That is, it's just like this:
let onClick = this.sayName;
onClick(); // Technically a click event is passed
to onClick
// but this doesn't matter for our purposes
We get an error. Because this is undefined. This is
extra confusing because in previous versions of React, React would "autobind" the event handler for you, so it would work. But at some point, Facebook decided to stop doing that, so ... here we are.
So how can we fix our component? We just do binding ourselves, like this:
<button onClick={this.sayName.bind(this)}>Say My
Name</button>;
Or with ES6 syntax:
<button onClick={() => this.sayName()}>Say My
Name</button>;
And it should work!
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.
Can I write React lifecycle methods as class properties?
I've been using class properties for a while as I like the fact that I no longer have to manually bind my methods, but I'd like to keep some consistency across my components and I'm wondering if there is any drawback on writing the React lifecycle methods as class properties
import React, { Component } from 'react';
class MyComponent extends Component {
render = () => {
return (
<div>Foo Bar</div>
);
}
}
export default MyComponent;
For example, is the context of this class property affected compared to the context in an equivalent method. Given that the render method in the above code is written as an arrow function, this concern seems relevant.
In a way, the true answer depends on your build pipeline and what the resulting Javascript output looks like. There are two primary possibilities:
Input Code
Let's start by saying you are writing the following before going through any sort of pipeline transformations (babel, typescript, etc):
class Test {
test = () => { console.log('test'); };
}
Output as class member variable.
In one possible world, your pipeline will also be outputting the test function as a member variable for the output class. In this case the output might look something like:
function Test() {
this.test = function() { console.log('test'); };
}
This means that whenever you write new Test() the test function is going to be recreated every single time.
Output as class prototype function
In the other major possibility, your pipeline could be recognizing this as a function property and escape it from the class instance to the prototype. In this case the output might look something like:
function Test() {
}
Test.prototype = {
test: function() { console.log('test'); }
}
This means that no matter how many times you call new Test() there will still be only one creation of the test function around in memory.
Desired behavior
Hopefully it's clear that you want your end result to have the function end up on the prototype object rather than being recreated on each class instance.
However, while you would want the function to not end up as a property, that doesn't necessarily mean you couldn't write it that way in your own code. As long as your build chain is making the correct transformations, you can write it any way you prefer.
Although, looking at the default babel settings (which your babeljs tag leads me to believe you are using) it does not make this transformation for you. You can see this in action here. On the left I've created one class with the function as a property and one class with the function as a class method. On the right hand side, where babel shows it's output, you can see that the class with the function as a property still has it being an instance-level property, meaning it will be recreated each time that class's constructor is called.
I did find this babel plugin, which seems like it might add this transformation in, but I've not used it for myself so I'm not positive.
In my experience, the most reason for writing a method as a class property is when the method will be passed as a callback, and you need it to always be bound to the instance. React lifecycle methods will always be called as a method, so there's no reason to bind them (and you incur a tiny memory penalty when you do). Where this makes a difference is when you're passing a function to a component as a callback (e.g. onClick or onChange).
Take this example:
class BrokenFoo extends React.Component {
handleClick() {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
The function represented by this.handleClick is not automatically bound to the component instance, so when the method tries to read the value of this.props it will throw a TypeError because this is not defined. Read this article if you're not familiar with this; the problem described in section 4.2 "Pitfall: extracting methods improperly" is essentially what's happening when you pass around a method without making sure it's bound correctly.
Here's the class, rewritten with the handler as a class property:
class HappyFoo extends React.Component {
handleClick = () => {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
Effectively, you can think of the handleClick definition in the second example as placing this code into the component's constructor (which is just about exactly the way Babel does it):
this.handleClick = () => {
alert(this.props.message);
}
This achieves the same thing as calling bind on the function (as described in the linked article) but does it a little differently. Because this function is defined in the constructor, the value of this in this.props.message is bound to the containing instance. What this means is that the function is now independent of the calling context; you can pass it around and it won't break.
The rule of thumb that I follow: by default, write methods as methods. This attaches the method to the prototype and will usually behave the way you'd expect. However, if the method is ever written without parentheses (i.e. you're passing the value and not calling it), then you likely want to make it a class property.
I see this a lot in ES6 React code
class Foo extends React.Component {
bar = () => {
console.log("bar")
}
baz() {
console.log("baz")
}
}
Seems like they both define methods bar and baz on Foo but how are they different.
The declarations differ in how the function are written and the context of this,
In the first syntax
bar = () => {
console.log("bar")
}
the function is written using Arrow function syntax.
An arrow function does not have its own this; the this value of
the enclosing execution context is used. Hence this keyword inside
this function will refer to the context of the React class
However the second declaration
baz() {
console.log("baz")
}
is a simple function and this keyword in this function refers to the context of the function itself.
So when you try to access React class Properties/functions like this.state or this.setState you will get an error in the second case(if you haven't used binding anywhere else for this function(example constructor)) whereas it would work in the first case since for an arrow function, this means the same thing within the function body as it does outside of it. Which means that if you use arrow functions within your component’s custom functions, they can use this and this.setState with no surprises.
Check this answer on why you need to bind functions in React classes
The previous answers are absolutely correct and are why you would want to be using arrow functions in react classes.
I just wanted to also point out a subtle difference that is a potential pitfall of their use in a class to avoid surprises...
Arrow functions defined on a class are added to the instance as a property that just so happens to be a function, whereas defining not as an arrow function will add the function as a method to the class's prototype.
In React components that will never be extended this is fine, but if the case arises you want to subclass a component, you will not be able to override an arrow function expecting to be able to call the base class via super, you can only overwrite it in its entirety
class Button extends React.PureComponent {
// class method - overridable in subclass
pressMethod: function(): void {
console.log('beep')
}
// instance property functions - only overwriteable in subclass
pressArrow = (): void => {
console.log('boop')
}
pressArrowTwo = (): void => {
console.log('blip')
}
}
class BigRedButton extends Button {
// class method - overides subclass one,
// can call superclass method via `super`
pressMethod: function(): void {
super.pressMethod() // prints 'beep' to console
console.log('BOOOOOOOOM!!!')
}
// instance property function
// cannot call superclass via `super` as lambda's have no prototype
pressArrow = (): void => {
super.pressArrow() // TypeError - not a function
console.log('BOOOOOOOOM!!!')
}
// completely overwrites instance property of same name in subclass
// doesn't try to access prototype so won't error but is limited
pressArrowTwo = (): void => {
console.log('BOOOOOOOOM')
}
}
For simplicity, both are equal:
bar = () => { ... }
this.bar =this.bar.bind(this)
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.