Clarification on React Class based component constructor - javascript

I am reviewing some React Js basics and I need some clarifications about the use of the constructor method within a react class based component. My normal practice is to declare a class based component and define state without using the constructor, like so:
import React, { Component } from 'react';
export class Testing extends Component {
state = {
example: "Hello World!!!"
}
render() {
return <div>
{this.state.example}
</div>;
}
}
export default Testing;
Since state is so easy to use without the (seemingly) pointless constructor, I began asking myself what the constructor was for. Just to clarify what I mean by the constructor, it is when you declare state like this instead of the above example:
constructor(props) {
super(props)
this.state = {
example: "Hello World!!!"
}
}
I found the suggestion in the react documentation that it is best practice to utilize the constructor, but it did not say why. After a little searching, I found a source that suggested that the constructor give you access to the props. I tested this and do see how the constructor gives access to props; however, the class can access props WITHOUT using the constructor, simply by stating this.props.myTest. I tested this as well by defining props in a parent component and drilling them down to the child component. I was able to access the props just fine with the this.props.myTest statement, no constructor necessary. So my question is, "What specifically does the constructor do, and when is it necessary (or is it ever necessary)?"

"when is the constructor necessary ?":
The constructor is not necessary, if you don't need it.
You might need a constructor e.g. if you need to bind a method to the class instance (so that this keeps pointing to the
class instance if you pass the function over to some other execution context):
export class Testing extends Component {
constructor( props ) {
super( props );
this.myMethod = this.myMethod.bind( this );
}
myMethod(){
console.log( this.props );
}
render() {
return <button onClick={ this.myMethod }>
click
</button>;
}
}
"What specifically does the constructor do" ?
A constructor "creates the instance" in javascript. So without a constructor, there would be no instance.
But also there is inherently always a constructor, even if you don't define one specifically.
You can define a constructor to overwrite the default constructor or to add some extra logic to be run
additionally to the super-constructor.
Accessing this.props in React
In javascript you could not access this.props without calling super(props).
Note that the constructor argument props and the instance property this.props are different "things".
Let's rename the constructor argument for now, to make it more obvious:
class SuperClass {
constructor( constructorArguments ) {
this.props = constructorArguments;
}
}
class DerivedClass extends SuperClass {
constructor( props ) {
super(); // <-- call the SuperClass constructor
}
method(){
console.log( this.props ); // <-- this.props is obviously never defined
}
}
const instance = new DerivedClass('some props');
instance.method();
But React "takes measures" to assign this.props anyway,
even when super(props) was never called.
But that happens later (after the constructor is done),
so you are able to access this.props in other methods later, but not inside the constructor:
export class Testing extends Component {
constructor( constructorArguments ) {
super(); // <-- super() is called without arguments
console.log( this.props ); // <-- this.props is undefined
}
render() {
console.log( this.props ); // <-- React sorted it out, now this.props is available
return null;
}
}
If you pass the constructor arguments to the super class constructor, then you can access this.props
inside the constructor:
constructor( constructorArguments ) {
super( constructorArguments ); // <-- super() is called with arguments
console.log( this.props ); // <-- this.props is available
}
React recommends to call super(props) anyway, to avoid introducing bugs with later changes.
I might add that it is very little extra code that doesn't do any harm.
Defining the state
class fields were not always available, so you had to define the state inside
the constructor, as the React example shows (like this.state = { value: 'value' }).
I personally think that it is probably ok now to use the class fields, but I always
tend to follow the official documentation, even if it might be outdated. There still
might be implications which I am not aware of.
See also: is-it-better-to-define-state-in-constructor-or-using-property-initializers

Related

Why is the variable declared in ComponentWillMount Undefined?

I have a component of this type. For simplicity, I have removed all unnecessary code.
This component displays a button when clicking on which the value of the variable I declared in ComponentWillMount should be displayed in the console, but when clicked, the console displays undefined, why?
'use strict';
class LoginFormComponent extends React.Component {
handleSubmit() {
console.log(this.model); //undefined
}
componentWillMount() {
this.model = 123;
}
render() {
console.log(this.model); //123
var styles = this.props.styles;
return (
<CM.MUI.FlatButton
style={styles.buttonStyle}
onClick={this.handleSubmit}
label={CM.gettext('Login')}/>
);
}
};
module.exports = LoginFormComponent;
You should be using componentDidMount for setting up the instance properties as the content in componentWillMount will not be in the instance scope as the component isn't mounted yet.
Also, use a => fat arrow function to get the access to this instance of your component.
Updated Code:
class LoginFormComponent extends React.Component {
handleSubmit = () => {
console.log(this.model); // 123
}
componentDidMount() {
this.model = 123;
}
render() {
console.log(this.model); //123
var styles = this.props.styles;
return (
<CM.MUI.FlatButton
style={styles.buttonStyle}
onClick={this.handleSubmit}
label={CM.gettext("Login")}
/>
);
}
}
export default LoginFormComponent;
Console
Demo: agitated-solomon-3rrow - CodeSandbox
More information
As explained in this demo: summer-violet-g4pyd - CodeSandbox, it looks like the way React works is as follows:
Constructor
componentWillMount
Render
componentDidMount
So after the render() is executed, the componentDidMount is getting executed and there's no change after any state change.
If you want something to be there, please put them in constructor().
Moreover, componentWillMount is deprecated and you should not use that in the next releases.
Because by writing onClick = {this.handleSubmit} you detach the function from the context, and in this function you have this - not your component
Try to write
onClick = {this.handleSubmit.bind (this)}
or
handleSubmit = () => {console.log (this.model)}
Code declared in componentWillMount will not be in the instance scope for a simple reason: The component isn't mounted yet. If you want to declare a global property in your class, just use componentDidMount or declare it inside the class body like any other method.
First, model seems to be a used as a state field, so as stated in the React docs :
Typically, in React constructors are only used for two purposes:
Initializing local state by assigning an object to this.state.
Binding event handler methods to an instance.
You should not call setState() in the constructor(). Instead, if your component needs to use local state, assign the initial state to this.state directly in the constructor:
So you should first define your state using:
constructor(props) {
this.state.model = 123; // or
this.state = { // commonly used syntax
model : 123
}
}
Then still according to the docs :
UNSAFE_componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering. Generally, we recommend using the constructor() instead for initializing state.
You are indeed using this to initialize your component's state. As pointed by others, you should instead use ComponentDidMount and use this.setState to modify the state as modifying the state directly with this.state.model is considered bad behaviour
Constructor is the only place where you should assign this.state directly. In all other methods, you need to use this.setState() instead.
Please check : https://reactjs.org/docs/react-component.html for more informations
I think this.modal refers to the FlatButton component instead, can you bind the handleSubmit to LoginFormComponent?
class LoginFormComponent extends React.Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit() {
...

Difference between ES5 and ES6 constructors in React.js

In React's source code, the prototype function has a signature with three parameters:
function Component(props, context, updater) {
But everytime I see somebody extends React.Component using modern ES6 features, they declare constructor with only props as follows:
constructor(props)
What is the difference between a prototype function and constructor literal? And where do the rest two arguments go?
You can obviously use all of the three params provided in the Component. But in general, only in an advanced case we use props, not every time. Similarly, you can use others.
Here's an example with context api:
ES6 class component
class ContextConsumer extends React.Component {
/*
contexTypes is a required static property to declare
what you want from the context
*/
static contextTypes = {
currentUser: React.PropTypes.object
};
render() {
const {currentUser} = this.context;
return <div>{currentUser.name}</div>;
}
}
ES6 class component with an overwritten constructor
class ContextConsumer extends React.Component {
static contextTypes = {
currentUser: React.PropTypes.object
};
constructor(props, context) {
super(props, context);
...
}
render() {
const {currentUser} = this.context;
return <div>{currentUser.name}</div>;
}
}
Example taken from this blog. I will suggest you to look into the blog to be more familiar with it.
Another param is updater which you may have used this.forceUpdate() and doing so invoke the updater. But the fact, we do not use updater directly in normal case. Though, I have not faced such case to use the updater inside the constructor. You might be able to figure out if you face some advanced case.
To be more frank and to use react with clever, I never even try to use props as far as possible. They are just provided for us so that we can utilize in lifecycle hooks when needed.
Okay, let me explain it a little bit with react code:
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
The Component function has 3 arguments which is being utilized inside the function. Later, its prototype forceUpdate is defined on which the updater is being hooked to enqueue the forceUpdate with enqueueForceUpdate. So, this.forceUpdate actually calling the Component updater and allows us to re-render the component. I hope, looking at its prototype method makes sense now.
What is the difference between a prototype function and constructor literal?
As far as I understand, you wanted to know why constructor is being called with the desired params.
The class/function constructor is used so that you can use an override to the function. For eg, passing props inside the constructor, you wanted to do an override with this. So, you explicitly notify the function to use the props as used inside the constructor.
So Es6 added the class keyword along with the constructor for it. It is however just syntactic sugar and is still based on the prototype model as before.
I don't know about the other two argos but I would guess it has to do with the function creating its own lexical scope.

When is it appropriate to use a constructor in REACT?

I understand the concept of constructors in OOP languages like C++. However, I am not entirely sure when to use a constructor in REACT. I do understand that JavaScript is object oriented, but I am not sure what the constructor is actually 'constructing'.
When rendering a child component, do you need a constructor in the child component? For example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
error: null
}
}
render () {
return (
<React.Fragment>
<ChildComponent data={this.state.items}></ChildComponent>
</React.Fragment>
)
}
}
I will keep the example short for the sake of brevity. But, why would do you need a constructor? And would you need a constructor in the child component for props?
It is possible that my ES6 knowledge is not up to snuff.
If you don’t initialize state and you don’t bind methods, you don’t need to implement a constructor for your React component.
The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.
Typically, in React constructors are only used for two purposes:
Initializing local state by assigning an object to this.state.
Binding event handler methods to an instance.
https://reactjs.org/docs/react-component.html#constructor
A constructor function is NOT required at all.
State initialization can be done directly in the body of a class and function can be assigned to properties as described below, Technically these are known as class properties, it may land up soon in native javascript, yet because almost all of us use Babel transpiler for React projects, we can use that syntax
class Comp extends React.Component {
/*
* generally we do not need to put the props in state, but even if we need to.
* then it is accessible in this.props as shown below
**/
state={ first: 1, second: this.props.second }
handler= (token) => {
this.setState(prevState => ({first: prevState.first+1}));
}
render() {
return <div onClick={this.handler}>{this.state.first} and {this.state.second } </div>
}
}
Read more details about class properties and removing constructor from react class component over here.
https://hackernoon.com/the-constructor-is-dead-long-live-the-constructor-c10871bea599
Linke in your example, it's useful use constructor when you need to initialize your state, or bind some event-listener-function
<element onClick={this.handler} />
this.handler = this.bind.handler(this); inside the constructor

ReactJS: What are some criteria for not needing a constructor in a component class?

I want to know the conditions where by a constructor is not needed in a component's class declaration. I figure it is for stateless components, but are there any other reasons? Would not having any functions inside the component (besides life cycle functions) be one, for example?
I think it would be appropriate to just leave here an excerpt from react docs (emphasis mine):
The constructor is the right place to initialize state. If you don't
initialize state and you don't bind methods, you don't need to
implement a constructor for your React component.
I usually only add a constructor if the component has a internal state I need to set up before it is used, otherwise I leave out the constructor. Having functions in the component doesn't affect my decision in this regard
You don't actually need a constructor at all in any case if you use the babel stage-2 preset, because it provides the class properties that effectively replace its usage:
class Component extends React.Component {
constructor() {
this.state = {};
this.handleClick = this.handleClick.bind(this);
}
handleClick() { console.log('click'); }
}
becomes
class Component extends React.Component {
state = {};
handleClick = () => console.log('click');
}
Ignoring this, a constructor is needed only if you need to bind component methods to its context, or if you need to initialize the state property.
Another case is if you need to do something with the this.props class property, but this is considered an anti-pattern by React.
class Component extends React.Component {
constructor() {
this.state = {
// don't do this! anti-pattern! duplication of source of truth!
duplicatedState: this.props.myName,
};
}
}

what does super() do without any arguments?

I'm learning react from the docs, but not sure what the super() does in this example. Usually, doesn't it take the arguments that are passed to making a new instance and then calls React.Component's constructor method to incorporate these arguments into the instance? What does it do without any arguments?
class LikeButton extends React.Component {
constructor() {
super();
this.state = {
liked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({liked: !this.state.liked});
}
render() {
const text = this.state.liked ? 'liked' : 'haven\'t liked';
return (
<div onClick={this.handleClick}>
You {text} this. Click to toggle.
</div>
);
}
}
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
);
In ES6, derived classes have to call super() if they have a constructor. In react, all components extend from the Component class.
You don't actually need a constructor for every ES6/react class. If no custom constructor is defined, it will use the default constructor. For base classes, it is:
constructor() {}
And for derived classes, the default constructor is:
constructor(...args) {
super(...args);
}
You also need to call super() before accessing this, since this is not initialized until super() is called.
There are a few reasons to use a custom constructor in react. One is that you can set the initial state within the constructor using this.state = ... instead of using the getInitialState lifecycle method.
You can also bind class methods inside the constructor with this.someClassMethod = this.someClassMethod.bind(this). It's actually better to bind methods in the constructor since they will only be created once. Otherwise if you call bind or use arrow functions to bind methods anywhere outside the constructor (like in the render method), it will actually end up creating a new instance of the function on every render. Read more about that here.
If you want to use this.props in the constructor, you need to call super with props as an argument:
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
If you don't, then this.props is undefined in the constructor. However, you can still access this.props anywhere else in the class outside the constructor without needing to do anything with it in the constructor.
The super keyword in JavaScript is used in order to call the methods of the parent class. By itself, super() is used within a constructor function to call the parent constructor function. For example:
class Animal {
constructor(age) {
console.log('Animal being made');
this.age = age;
}
returnAge() {
return this.age;
}
}
class Dog extends Animal {
constructor (age){
super(age);
}
logAgeDog () {
console.log(`This dog is: ${ super.returnAge()} years old`);
}
}
const dog = new Dog(5);
console.log(dog);
dog.logAgeDog();
In this example we have a Dog class which extends an Animal class. The Dog class uses the super keyword twice. The first occurence is in the constructor, when super() is used in the constructor it will call the parent class constructor. Therefore we have to give the age property as an argument to it. Now the Dog successfully has an age property.
We can also use super outside of the constructor in order to access the parent's 'class' (i.e. prototype) properties and methods. We use this in the logAgeDog function located in the Dog class. We use the following code:
super.returnAge();
You should read this as:
Animal.returnAge(); // superClass.returnAge()
Why do I need this in React?
You need the super() keyword in React only when implementing a constructor. You have to do the following:
constructor(props) {
super(props);
// Don't call this.setState() here!
}
the parent class which is named Component needs to do some initialization on its own in order for React to work fine. If you implement a constructor without a super(props) call this.props in Component will be undefined which can lead to bugs.

Categories