When can we avoid using a constructor to initialize state? - javascript

In the example given on the reactnativeexpress website here, state = {count: 0} is used in place of a constructor to initialize the count variable.
In the official Facebook docs here, they've said
In general, you should initialize state in the constructor, and then call setState when you want to change it.
So I created a constructor to initialize count in the same manner:
constructor(props) {
super(props);
this.state = {count: 0}
}
The code seems to work the same way (at least on the surface), so what's the difference between creating a constructor and initializing count as in the example?

In the example that you attach as a link they use the ES Class Fields which is a proposal (currently in stage 2).
That means you will need some extra presets / plugins of babel (or any other JavaScript compiler) to support it as it's not part of the official specifications of ECMAScript yet.
In the other example, you use the class constructor which is part of ES2015.
Both will get you the same results, a property attached to the instance of the class.
which is basically just a syntactic sugar for the prototype pattern.

This has more to do with ES6 Classes.
getFullName() {
return this.props.firstName + this.props.lastName;
}
The above function will throw an error saying props is not defined.
getFullName = () => {
return this.props.firstName + this.props.lastName;
}
The above code will pass the context of this into the function and will work good.
Similarly, your
state = {count: 0}
is executed in the context of the class [using appropriate this ].
The same state needs to be called using this.state within the constructor to bind it to appropriate context.

Related

Accessing this inside super() method - javascript

The code is as follows
class ComposerForm extends BaseForm {
constructor(formsObject, options) {
super({
...options,
setup: {},
});
this.formsObject = { ...formsObject };
}
..
}
Now i have a new form
class PreferencesForm extends ComposerForm {
constructor(company, options = {}) {
super(
{
upids: new UpidsForm(company).initialize(),
featureSettings: new FeatureSettingsForm(company)
},
options
);
}
}
When initialising the FeatureSettingsForm, i need to pass the Preference form along with the company object
Something like
{
featureSettings: new FeatureSettingsForm(company, {prefForm: this})
},
so that i can access the preference form inside featureSettings form.
But this cannot be done since this cannot be accessed inside the super method.
Any idea on how to achieve this?
If I understand you right,
You need to pass a FeatureSettingsForm instance in the object you're passing to super (ComposerForm) in the PreferencesForm constructor, and
You need this in order to create the FeatureSettingsForm instance
So you have a circular situation there, to do X you need Y but to do Y you need X.
If that summary is correct, you'll have to¹ change the ComposerForm constructor so that it allows calling it without the FeatureSettingsForm instance, and add a way to provide the FeatureSettingsForm instance later, (by assigning to a property or calling a method) once the constructor has finished, so you can access this.
¹ "...you'll have to..." Okay, technically there's a way around it where you could get this before calling the ComposerForm constructor, by falling back to ES5-level ways of creating "classes" rather than using class syntax. But it general, it's not best practice (FeatureSettingsForm may expect the instance to be fully ready) and there are downsides to have semi-initialized instances (that's why class syntax disallows this), so if you can do the refactoring above instead, that would be better. (If you want to do the ES5 thing anyway, my answer here shows an example of class compared to the near-equivalent ES5 syntax.)

Why exactly is bind(this) necessary in React constructors? [duplicate]

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.

How ES6 classes compiled?

While I was looking to react native animations documentation, I read something interesting to me. Code piece:
class FadeInView extends React.Component {
state = {
fadeAnim: new Animated.Value(0), // Initial value for opacity: 0
}
// Other parts of code piece ...
// https://facebook.github.io/react-native/docs/animations.html
}
Let's break down what's happening here. In the FadeInView constructor, a new Animated.Value called fadeAnim is initialized as part of state.
In the above statement docs mentioning that state is initialized in constructor. However there is no constructor in this code piece. From the knowledge of other languages like c# and c++ etc. variables can be declared and initialized in classes without constructor.
I actually didn't understand underlying process in javascript how these variables declared and initialized in es6 classes.
Why he is mentioning constructor while there isn't any constructor.
How es6 classes compiled? (This might be long, overall picture of that can be enough for my needs)
The state is a class property, currently not an ES6+ standard feature.
Therefore you need the Babel Stage 2 preset in order to transform class properties.
Currently, that plugin transformation moves the class properties into the constructor. Check the babel repl output
Simplified Babel output:
function FadeInView() {
this.state = {
fadeAnim: new Animated.Value(0) // Initial value for opacity: 0
};
};
More info related to class field declarations
Class properties (a js proposal yet) are actually moved into the constructor:
class FadeInView extends React.Component {
constructor() {
this.state = {
fadeAnim: new Animated.Value(0), // Initial value for
opacity : 0
};
}
}

Class properties for react lifecycle methods

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.

JSX props should not use .bind() - how to avoid using bind?

I have a container that I need to change the UI form showing the form or showing a success page.
The container has a state.showSuccess and I need the MyFormModule to be able to call the container to change the state.
The below code works but I'm getting the following warning:
JSX props should not use .bind()
How can I get this to work without using .bind()?
...
const myPage = class extends React.Component {
state = { showSuccess: false };
showSuccess() {
this.setState({
showSuccess: true,
});
}
render() {
const { showSuccess } = this.state;
if (showSuccess) {...}
....
<MyFormModule showSuccess={this.showSuccess.bind(this)} />
You should first understand WHY this is a bad practice.
The main reason here, is that .bind is returning a new function reference.
This will happen on each render call, which may lead to a performance hit.
You got 2 options:
Use the constructor to bind your handlers (this will run only once).
constructor(props) {
super(props);
this.showSuccess = this.showSuccess.bind(this);
}
Or create your handlers with arrow functions so they will use the
lexical context for this, hence you won't need to bind them at
all (you will need a babel plugin):
showSuccess = () => {
this.setState({
showSuccess: true,
});
}
You should not use this pattern (as others suggested):
showSuccess={() => this.showSuccess()}
Because this will as well create a new function on each render.
So you may bypass the warning but you are still writing your code in a bad practice design.
From the ESLint docs:
A bind call or arrow function in a JSX prop will create a brand new
function on every single render. This is bad for performance, as it
will result in the garbage collector being invoked way more than is
necessary. It may also cause unnecessary re-renders if a brand new
function is passed as a prop to a component that uses reference
equality check on the prop to determine if it should update.
Use an arrow function when defining showSuccess
showSuccess = () => {
this.setState({
showSuccess: true,
});
}
Use an arrow function since they automatically inherit the this context of wherever they are defined.
showSuccess={() => this.showSuccess()}
Here is a link to the facebook documentation on this subject, which lists this method among others as a solution. Interestingly, they also list using .bind in the prop as one of the solutions, even though it produces a warning when actually used.
From that documentation, you'll note that this is a potential performance issue, since the function will be recreated on every render:
Note:
Using an arrow function in render creates a new function each time the
component renders, which may have performance implications (see
below).
But also from the same link:
Is it OK to use arrow functions in render methods? Generally speaking,
yes, it is OK, and it is often the easiest way to pass parameters to
callback functions.
If you do have performance issues, by all means, optimize!
So I would say if your component will be re-rendering very frequently, you should use one of the other solutions: bind in the constructor, or define the method with an arrow function in the first place. But if not, use whatever method seems cleanest to you.

Categories

Resources