Arrow vs classic method in ES6 class - javascript

Is there any reason to write classic syntax of ES6 methods?
class MyClass {
myMethod() {
this.myVariable++;
}
}
When I use myMethod() as callback on some event, I must write something like this (in JSX):
// Anonymous function.
onClick={() => { this.myMethod(); }}
// Or bind this.
onClick={this.myMethod.bind(this)}
But if I declare method as arrow function:
class MyClass {
myMethod = () => {
this.myVariable++;
}
}
than I can write just (in JSX):
onClick={this.myMethod}

The feature you are using is not part of ES6. It's the class fields proposal. It allows you to initialize instance properties without having to write a constructor. I.e. your code:
class MyClass {
myMethod = () => {
this.myVariable++;
}
}
is exactly the same as
class MyClass {
constructor() {
this.myMethod = () => {
this.myVariable++;
};
}
}
And this also shows you what the difference is between a normal class method an a method created via a class field:
A normal method is shared between all instances of the class (it is defined on the prototype)
A "class field method" is created per instance
So all the same as reasons as presented in Use of 'prototype' vs. 'this' in JavaScript? apply, but in short:
Use "class field methods" if you need a method per instance. Such is the case for event handlers that need to access the current instance. Access to this also only works if you are using an arrow function.
Use normal class methods in all other cases.

Related

Calling regular function vs. arrow function before their declaration in a class

If I write a React class like this:
class SomeClass extends React.Component {
state = {
someState: this.someRegularFunction(),
someOtherState: this.someArrowFunction()
};
someRegularFunction() {
return "someText";
}
someArrowFunction = () => {
return "someOtherText";
};
}
Webstorm code assistance warns about the calling of arrow function this.someArrowFunction() saying:
Field 'someArrowFunction' is declared after 'state' and is possibly
not assigned yet
If does not warn about the call of the regular function this.someRegularFunction().
And Webstorm is correct, the execution fails when calling this.someArrowFunction() with:
TypeError: _this.someArrowFunction is not a function
I've been looking for some documentation explaining this behavior, but have been unable to find any.
Why can you call regular functions, but not arrow functions, before their declaration inside a class?
Because that code is functionally identical to this:
class SomeClass extends React.Component {
constructor(...args) {
super(...args);
this.state = {
someState: this.someRegularFunction(),
someOtherState: this.someArrowFunction()
};
this.someArrowFunction = () => {
return "someOtherText";
};
}
someRegularFunction() {
return "someText";
}
}
Field definitions are processed in source code order, when the instance is created. It's as though they were inserted into the constructor before any other code (in a base class) or just after the call to super (in a subclass).
In contrast, someRegularFunction is a method of the prototype, which is created when the class definition is evaluated, not later when the instance is created.
This is covered by the proposal for the class fields feature, in the specification text. (Reading spec text is not for the faint-hearted, though! :-) )
Side note: It's arguably a matter of style, but if you're doing that arrow function so that it can use this without your worrying about how it's called (for instance, as an event handler), you might consider making it a method and then using bind in the constructor (or effectively in the constructor) instead:
class SomeClass extends React.Component {
someFunction = this.someFunction.bind(this);
state = {
someState: this.someRegularFunction(),
someOtherState: this.someFunction()
};
someRegularFunction() {
return "someText";
}
someFunction() {
return "someOtherText";
}
}
That plays more nicely with testing code that may need to mock the function (by replacing it on the prototype).
But again, it's arguably a matter of style.

es6 function declaration in React class

Whats the difference between functions declared with const and functions declared without let or const and a function declared otherwise in an ES6 class?
class App extends Component {
submitFood = () =>{
// some code
}
Why does the above work OK but the declaration below give an error:
class App extends Component {
const submitFood = () =>{
// some code
}
First of all: None of the examples you provided is valid ES6. The grammar rules for ES6 classes only allow methods definitions inside the class body. I.e.
class MyClass {
method1() {}
method2() {}
}
The first example however is making use of the class fields proposal. This proposal extends the existing grammar to allow the definition of properties of the form
class MyClass {
someProperty = value;
}
These assignments are not evaluated at class definition time but at instantiation time. It's syntactic sugar for assigning properties in the constructor:
class MyClass {
constructor() {
this.someProperty = value;
}
}
Your second example is simply invalid syntax because there are no grammar rules that allow putting let or const before the class field.
Keep in mind that class fields are not variable declarations or variable assignments.
const submitFood = () =>{
// some code
}
Is the creation of function (submitFood=() {}) and the creation of a variable so usual rules in variable hosting blocked let and const.
so fail since submitFood() is not defined. (It will throw a ReferenceError).

Wrapping a constructor with javascript method decorator

Referenced:
Override constructor with an class decorator?
http://blog.wolksoftware.com/decorators-metadata-reflection-in-typescript-from-novice-to-expert-part-ii
I'd like to wrap a classes constructor with a custom one when applying a method decorator (ie in the same way you could do with a class decorator). I require this as I have another component that will call into the class, and will execute the method that was decorated. Its not hard to have the component execute the method that was decorated, however because the decorator is run before the class is instantiated the decorated method will not be associated with a class instance and therefore will not have access to any of the classes state (ie this == undefined). So I want to be able to provide the method reference to the component during class instantiation (ie constructor) so that it is bound to the current instance.
So I'd like to do this (typescript):
class Foo {
constructor(private value) { }
#execute
bar() {
return this.value;
}
}
Which would have the same affect as doing this:
class Foo {
constructor(private value) {
StaticComponent.AddReference(this.bar().bind(this));
}
bar() {
return this.value;
}
}
If my other component has a reference to bar() it should be able to execute it with bar having full access to its instance.
I've tried to override the prototype of the target etc in a similar way you might do so for a class decorator, but couldn't get that to work.

Why do event handlers not get the right handle to this? [duplicate]

This question already has answers here:
Unable to access React instance (this) inside event handler [duplicate]
(19 answers)
Closed 5 years ago.
I understand the concept of binding event handlers in order for them to get the proper reference to this. My question is, why does referencing a class's method not provide it the proper this?
Do ES6 classes not automatically provide this on class methods?
class MyComponent extends Component {
clickHandler(event) {
console.log('this', this);
}
render() {
return <button onClick={this.clickHandler}>Button</button>;
}
}
As expected, the above code will print null for the value of this. However, explicitly calling the handler will give the correct value, i.e. <button onClick={event => this.clickHandler(event)} />. Why does the handler have the correct reference to this using the arrow function?
I can hypothesize a bunch of answers, but I'd like the correct one.
Why do event handlers not get the right handle to this?
They do! They get a reference to the element you hooked the event on, as normal.
Do ES6 classes not automatically provide this on class methods?
They don't, no; ES2015 ("ES6") classes use the same fundamental prototypical inheritance and this assignment as ES5 and before, with different syntax (and a couple of new features).
However, explicitly calling the handler will give the correct value
Right, for the same reason that in this code, example is called with this referring to x:
x.example();
...but in this code, example is called with this being undefined (in strict mode; it would be the global object in loose mode):
let e = x.example;
e();
Using class doesn't change the need for this management.
To make this to refer to the component class, you need to either bind this to it, or use an arrow function that closes over the this to use:
Binding (a fairly common pattern):
class MyComponent extends Component {
constructor(...args) { // ***
super(...args); // ***
this.clickHandler = this.clickHandler.bind(this); // ***
} // ***
clickHandler(event) {
console.log('this', this);
}
render() {
return <button onClick={this.clickHandler}>Button</button>;
}
}
Using an arrow function instead (also fairly common):
class MyComponent extends Component {
clickHandler = event => { // ***
console.log('this', this);
}; // ***
render() {
return <button onClick={this.clickHandler}>Button</button>;
}
}
That latter option assumes you're transpiling with support for the upcoming public class fields, which aren't standard yet. (The proposal is still at stage 2 as of this writing.) Instead of creating a property on the prototype, it creates an instance field with an arrow function, which closes over this (which is a reference to the component instance).

Why do I have to .bind(this) for methods defined in React component class, but not in regular ES6 class

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.

Categories