How does React's Component's constructor really work? - reactjs

I appreciate it if anybody can explain why the following code works.
I created a NumberListBase React component. Then created another one, named NumberList and derived it from NumberListBase.
In the constructor of the two components I purposely don't pass 'props' argument to the parent class when calling super().
class NumberListBase extends React.Component {
constructor(props) {
super();
Log("NumberListBase.ctor: ", this.props, props);
}
}
class NumberList extends NumberListBase {
constructor(props) {
super();
Log("NumberList.ctor: ", this.props, props);
}
render() {
const numbers = this.props.numbers;
const listItems =
numbers.map((n) => <li key={`${n}`}>{n}</li>);
return (<ul>{listItems}</ul>);
}
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
I expected that render() will fail, because this.props would be undefined in it.
The log messages I put in the constructors shows clearly that the 'props' argument and 'this.props' are 'undefined' in NumberListBase constructors.
But, in complete amazement, the component rendered correctly and showed the numbers, meaning that the 'props' got to React.Component somehow and React.Component could put it inside 'this.props'!
This is a codepen I created for this question.
https://codepen.io/mansoor-omrani/pen/oKaMXV
I also created a jsfiddle snippet to test how constructors work in a class hierarchy.
https://jsfiddle.net/omrani/thnj2zu4/11/
I checked React.Component source code to see how this class is defined.
https://github.com/facebook/react/blob/master/packages/react/src/ReactBaseClasses.js
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;
}
React.Component is just a simple function. I'm still wondering how, where and when 'this.props' was set!

When you call super() without the props, you’ll still be able to access this.props in the render and other methods because React assigns props on the instance right after calling your constructor.
We pass props into the super method only when we need to use props inside the constructor.
The reason why they implemented this behaviour is unclear, probably for future compatibility reasons, as Robin Pokorny replied to the below question.
ref: What's the difference between "super()" and "super(props)" in React when using es6 classes?

Related

Assigning props to variable in React class component

I have a simple react component. I would like to assign a variable inside the component method to props. I've tried this method works with functional components:
class Pets extends React.component {
constructor(props) {
super(props)
}
const {dog, cat, frog} = props
// the code on the line above does not work
render() {
return (
<div>
{dog.name}
{cat.name}
</div>
)
}
}
That will not work. props is a property on the instance of the component you created with <Pets /> JSX for example. It needs to be inside the instance methods of the class Pets. It will work if you do it in the render method by const {dog, cat, frog} = this.props. props in the class body as you have now in the question is not what them expected to be.
Inside the constructor function just const {dog, cat, frog} = props will work, because here props object is received as an argument. so this.props (after the super(props) line)andprops` is same object.
Generally you unpack props where you need them, for example you need those inside the render to create some output, so you unpacked it there. I tried to explain in this answer what props means in the class body, and instance methods body, how you can access the props in different methods etc. But as xadm said, unpacking them inside the constructor by thinking you will access them later in other methods will not work due to the scope boundary, unless you store them as a property to the instance again using this.dog = props.dog. But this is an horrible idea, never do this. Just unpack them when you need any property from props object in that place only.

Constructor in Reactjs

I am in react.js. I am learning react.js. I would like to know when to use constructor like below and when not.
constructor(props) {
super(props);
}
1) Initialising state
class MyClass {
constructor(props) {
// when you want to iniitialize state
// (ex. below is setting initial value for 'inputBoxValue')
this.state = {
inputBoxValue: 'initial value',
};
}
}
2) Using this in constructor
class MyClass {
constructor(props) {
// when you want to use `this` in `constructor`
// super needs to be called first
super();
// that means, when you want to use `this.props`
// in `constructor`, call it like below
super(props);
}
}
3) Providing ref callback for accessing DOM
class MyClass {
constructor(props) {
// when you want to create ref
this.myElementRef = (ref) => {
this.myElement = ref;
};
}
}
5) Initializing Third-party libraries
class MyClass {
constructor(props) {
// initialize third party libs, primarily non react libs
this.myHelper = new MyHelperLibrary();
// note: you can still access props without using `this`
this.myHelper2 = new MyHelperLibrary(props.environment);
}
}
4) Binding some context(this) in case you want class method to be passed in props to children.
class MyClass {
constructor(props) {
// when you want to `bind` context to a function
this.myFunction = this.myFunction.bind(this);
}
}
If you have no useful things of your own to do in the constructor, then you don't need one.
Examples of useful things include:
setting initial state based on the initial props
binding functions to this
The constructor in the question is what would happen by default, if you didn't write one of your own. So if you have no lines of your own to add, then you can safely remove it from your component class.
In a React component, constructor is used to initialize the state. It's the right place to do it.
When implementing constructor for a react component which derived from React.Component, super method should be called.
For more detailed information, I suggest to look for this React document: https://reactjs.org/docs/react-component.html#constructor
Assuming below React Component tree
<A>
<B dataProps={this.state.data}/>
</A>
this.state.data is getting passed component to component B from parent component A.
Now,incomponent B,if you want to access this.state.data,you need to use
this.props.dataProps
Coming to your constructor quetition,
case 1 :
constructor(props) {
super(props);
}
in this case,
console.log(this.props.dataProps) will log dataProps mapped to this.state.data because
this.props initilised with props coming from parent component A i.e. dataProps value for this.state.data
case 2:
assume in constructor, there is call to only super() without props.
constructor() {
super();
}
In this case,
console.log(this.props.dataProps) //undefined bcause this.props is undefined
When you are getting the props from the parent component and you are using these props in the constructor to initialize the state then you have to use the syntax:
constructor(props) {
super(props);
}
And if you are not using the props to initialize the state then you can use the syntax:
constructor() {
super();
}

react-constructor or componentWillRecevieProps

i use the react for two month, something the react will do constructor, sometime the react will do componentWillRecevieProps.
eg:
renderGroups(){
return this.props.groups.map( (group, i) => {
return <PickerGroup key={i} {...group} onChange={this.handleChange} groupIndex={i} defaultIndex={this.state.selected[i]} />;
});
}
this write will do PickGroup constructor every time
Its confusing what you are asking here, but you mentioned React will receive a constructor() function and sometimes it receives a componentWillReceiveProps.
The purpose of constructors() in React is to initialize state. You initialize state when the component is first created. After that, you use state inside the render() method and then at some point in the future, you update that state with setState().
Unlike the constructor() function, the render() method is required for every single React component you create, otherwise, React will throw an error. The constructor() function is not required by React, but it is implemented in a class-based component like so:
class App extends React.Component {
constructor() {
}
// React says we have to define render()
render() {
window.navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(err) => console.log(err)
);
return <div>Latitude: </div>;
}
};
The constructor() function is particular to the JavaScript language, not to React.
In a JavaScript class, the constructor() function is the first function that is going to be called anytime an instance of this class is created.
Anytime we create a new instance of the App component and display it on the screen, this constructor() function is going to be automatically and instantly called before anything else.
Now, to get to what I believe is your question, this is not the only way to initialize state in React.
Before I get into the other way to initialize state, you ought to know that when we define the constructor method, it will automatically be called with the props object and yes this is the same props object you may have seen with functional components and with a class-based component it looks like this:
class App extends React.Component {
constructor(props) {
}
// React says we have to define render()
render() {
window.navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(err) => console.log(err)
);
return <div>Latitude: </div>;
}
};
When you use the constructor(props) function, there is one required step which is adding super(props);. Why do we have to add this?
Well, remember, I am talking about class-based components here and keep in mind that as a class-based component our App component above is extending or borrowing functionality from the React.Component base class here.
This base class has a constructor() function of its own that has some code inside of it, to setup our react component for us.
When we define a constructor() function inside our App class, we are essentially, overriding or replacing the constructor() function that is inside the React.Component class, but we still need to ensure that all the setup code inside of the React.Component constructor() function still gets called.
So to ensure that the React.Component’s constructor() function gets called, we call super(props);. super(props) is a reference to the parents constructor() function, that’s all it is.
We have to add super(props) every single time we define a constructor() function inside a class-based component.
If we don’t we will see an error saying that we have to call super().
The entire reason for defining this constructor() function is to initialize our state object.
So in order to initialize my state object, underneath the super(props); call I am going to write:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// React says we have to define render()
render() {
window.navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(err) => console.log(err)
);
return <div>Latitude: </div>;
}
};
This is our state object that will eventually contain some different pieces of data, some different properties that are very important and relevant to the component being put together.
So lets say I initialized my state with a property called lat for latitude because this is a geolocation application I am working on, so now my initialized state looks like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {lat: null};
}
I set the value of the lat property to null because I don't have a latitude yet and the latitude value will eventually be a number and in such a case when you know the value will be a number that you don't have yet, you can default it to be null.
Now, let's say we get an error on this app and we cannot get any data or state at all. We can add another property to state called errorMessage and we can default it to be an empty string like so:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {lat: null, errorMessage: '' };
}
Alright, now lets talk about the alternate to the constructor(), by changing this.state and refactoring to state = {lat: null, errorMessage: '' }; and then deleting the constructor() entirely.
class App extends React.Component {
state = {lat: null, errorMessage: '' };
}
This is equivalent to using the constructor() function and I can prove this by going to https://babeljs.io/ and adding the above code to the left panel and seeing how Babel translates it into a constructor() anyway and it translates it back to this.state = like so:
If you want to reproduce this yourself, please pay attention to what presets have been ticked on the left hand side of Babel tool.
Lastly, you mentioned the alternative to the constructor() would be componentWillReceiveProps, but the alternative is actually what I just implemented above.
Please keep in mind the difference between initializing state and utilizing props. Everything described above is initializing state which is what the constructor() is for.
In React, you will see different lifecycle methods called, but typically speaking what you will see most of the time is componentDidMount() which automatically gets called one time when the component first gets rendered on the screen and you can place logic in there to do initial data loading and other operations you might want to do one time when the component first boots up.
The other lifecycle methods you will typically see most often in addition to componentDidMount() is componentDidUpdate() and componentDidUnmount(), but these are not alternatives to initializing state.
The constructor() function is the only lifecycle method used for state initialization, yes, it is a lifecycle method and so is the render() method.
First of all Constructor and ComponentWillReceiveProps are used for two different purposes. As per the React DOCS
Constructor:
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.
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.
componentWillReceiveProps:
componentWillReceiveProps() is invoked before a mounted component
receives new props. If you need to update the state in response to
prop changes (for example, to reset it), you may compare this.props
and nextProps and perform state transitions using this.setState() in
this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you
only want to handle changes. This may occur when the parent component
causes your component to re-render.
React doesn't call componentWillReceiveProps with initial props during mounting. It only calls this method if some of component's
props may update.
So in general you may want to call take actions in your component when the parent component has sent different props, in this case you will use componentWillReceiveProps as well as the constructor to initialse the state with props.
In your case since your are using a map function you are again and again mounting the same component whenever the renderGroups function is being called and hence the constructor is called everytime instead of componentWillReceiveProps
I hope you, I was able to explain

What exactly is a "Prop" in React?

I just started using React native on iOS to see how it feels like and I have a silly question..
I see everyone talking about "Props", whenever I read an article or a tutorial, the author uses this term a lot and it's the same in the code. For instance, in class declarations I often see constructors like :
class MyClass extends Component {
constructor(props) {
super(props);
this.state = { message: '' };
}
}
I can't find a clear explanation of what a prop is, could anyone enlighten me about that ?
props are the values that you pass down to your React component. So whenever you have a component you'll see something like this:
<MyComponent value1={value1} isTrue={true} />
In addition to the answer of Keith, below you can find the non-JSX version that uses a 'color' property.
// Output (JS):
var app = React.createElement(Nav, {color:"blue"});
React Props are read-only arguments passed from one React Component to another (down the component hierarchy).
Let's say you have two components - App and List. Then, inside App we have a list with information from developers. If we wanted to pass the array to the List component, we'd do something like this (<List list={developers} />)
import React from 'react';
const App = () => {
const developers= [
{
id: 1,
name: 'Randy'
},
{
id: 2,
name: 'Tiago Peres'
},
];
return (
<div>
<h1>Developers in StackOverflow</h1>
<List list={developers} />
</div>
);
};
const List = props =>
props.list.map(item => (
<div key={item.id}>
<span>{item.name}</span>
</div>
));
export default App;
This would be the result
Using this, we don't need to have too much information in the global scope.
If you want to know more about it, there's a great article from Robin Wieruch that i would suggest you to read.
To understand the specific case you mention, one must know some basics of React and also ECMAScript 6 (ES6). Based in content from W3Schools's React Tutorial,
A class created with a class inheritance inherits all the methods from another class.
To create a class inheritance we use the extends keyword.
You're creating a class named MyClass which will inherit the methods from the Component class.
The properties of a class are assigned inside a constructor() method
The constructor function is called automatically when the object is initialized.
The super() method refers to the parent class and so executes the parent component's constructor function.
If the component has a constructor function, the props should always be passed to the constructor and also to the React.Component via the super() method.
super(props) would call the Component constructor passing in props as the argument.
While most likely you could just use super(), using super(props) ensures this.props is set even before the constructor exits.
Even though in your case the state object has only message property, you can have as many as you like.
You can use this.state.message anywhere in the component.
Props : Props is nothing but property of component and react component is nothing but a JavaScript function. Props are immutable. You can pass props between components.You can pass props from parent component to child component directly. For passing from child to parent you need use concept of lifting up states.
class Parent extends React.Component{
render()
{`enter code here`
return(
<div>
<Child name = {"Sara"}/>
</div>
);
}
}
class Child extends React.Component{
{
render(){
return(
<div>
{this.props.name}
</div>
);
}
}

Is constructor needed for a ReactJS component class

I'm using ReactJS in Typescript. Do I need the "Constructor" code below? It works fine without it and I looked at the trans-piled JavaScript and it appears to add it in automatically anyway.
interface myProps {
children?: any;
}
class MyButton extends React.Component<myProps, {}> {
constructor(props: myProps) { //Needed ???
super(props);
}
render() {
return (<div>
<button>
{this.props.children}
</button>
</div>);
} //end render.
} //end class.
No, you don't need to.
In fact, you could write a simple component like this as a function.
const MyButton = (props) => {
return (
<div><button>{props.children}</button></div>
);
};
As mentioned in the accepted answer:
const MyButton = (props) => {
return (
<div><button>{props.children}</button></div>
);
};
This works but it's not the same - not at all. This is constructing a StatelessComponent (without state, without lifecycle-hooks, it's just a plain function, returning only the JSX or the part of the "render" function).
If you need state or lifecycle-hooks, you must extend from React.Component - like it is already done in the question.
To actually answer your question - yes constructor is needed. That's because you are extending of an existing class which is already asking for an initial property ("props") that must be given on the construct -> because it is a react class, react will call internally something like
new MyButton(props);
He will always give you the props object into the instantiated Component. Now, by extending this existing react component class, you must achieve the same - or you will end up missing your "props" object. To make it possible, that one can still pass "props" to your newly defined component, you have to define the props also on your constructor, so you have to write this:
constructor(props: myProps) { .. }
Otherwise, you can't pass anything when you call "new MyButton(props)" -> well, this will not except or bring an error, but "props" will just be "null" in your further code of "MyButton".
Last but not least - you have to call the "super" in an extended version.
super(props);
Otherwise, you won't pass the given "prop" object to the basic class which you're extending from. Here, it could work too without it, but - then "props" will be "null" in any code of "React.Component" itself.
So, yes it is needed!
Basically you can answer this question yourself by simply using a debugger - just open your web-dev tools, jump to your component code, make a breakpoint in your constructor and watch closely what is happending and what is passed ;) Then, delete the property in the constructor once.. and remove the super call once... how will it except/break apart? ;)
Update:
The other question you can always ask yourself when creating new react components: do i need state? do i need lifecycleHooks?
If one of those is yes, you'll have to extend from React.Component - because only the basic class is giving you this sugar. But as can say, no, i don't need any of those - always go with a StatelessComponent:
const MyComp = (props) => (
<div></div>
)
And try to avoid local component states too - you can do much better with one global state trough react-redux and selectors ;)
Constructor and Super are no longer necessary in react. State can be defined without a constructor and lifecycle hooks also work fine. This is a result of Babel transpiling the components that way: http://2ality.com/2017/07/class-fields.html
In traditional ES6 this is currently not the case.
Today, the constructor is needed only when you must initialize a component with a state from props.
import React from 'react'
class AChildComponent extends React.Component {
constructor(props){
super(props)
this.state = {
myState: props.myStateFromProps
}
}
render(){
return(
<div>
<p>{this.state.myState}</p>
</div>
)
}
}
export default AChildComponent

Resources