I have a question. Perhaps a little silly.
For example i have class, with state and baseState, who linked to state inside construcor
class SomeStrangeClass extends Component {
constructor(props){
super(props);
this.state = {
confirmError:[],
};
this.baseState = this.state;
}
componentWillReceiveProps(nextProps){
console.log(this.state); //here i can have, for example, 3 confirmErrors...
console.log(this.baseState); // here i still have an empty array
this.setState(this.baseState);
}
...some strange code
}
Of course it is good for me. Simple reset of state)))
But..why this.basestate is empty, meanwhile this.state is not.
Why javascript behaves as if did not create a reference to the object?
Thank you very mutch!
With
this.baseState = this.state;
you are defining the completely new property of that React.Component class.
I the constructor means at the specific time when you initialize your React.Component object they will be equal.
It is a JavaScript feature to add new properties to any object dynamically.
Later on, when you execute setState() method of the Component class only the predefined state property will update and not the other one.
Here is the part of the source code for the React.Component :
/**
* Module for creating composite components.
*
* #class ReactClass
*/
var ReactClass = {
createClass: function(spec) {
// To keep our warnings more understandable, we'll use a little hack here to
// ensure that Constructor.name !== 'Constructor'. This makes sure we don't
// unnecessarily identify a class without displayName as 'Constructor'.
var Constructor = identity(function(props, context, updater) {
// This constructor gets overridden by mocks. The argument is used
// by mocks to assert on what gets mounted.
if (__DEV__) {
warning(
this instanceof Constructor,
'Something is calling a React component directly. Use a factory or ' +
'JSX instead. See: https://facebook.github.io/react/warnings/legacy-factories.html'
);
}
// Wire up auto-binding
if (this.__reactAutoBindPairs.length) {
bindAutoBindMethods(this);
}
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
this.state = null;
just to visualize what is inside the component.
When you do the assignment this.baseState = this.state, you are assigning the value of this.state at that time to the variable this.baseState, essentially making a copy. If this.state is updated with new variables after this, it does not reflect in the copy you made before.
Related
Right now I'm working on a component that needs references to scroll to specific items. However when the component updates all my refs become null. Now I know this is actually intentional behavior because of memory leaks (etc.) but I don't care in this case.
Right now all my create ref code is being called in the constructor, but when I try and recreate the refs they're always null.
I generate the refs in the constructor of my component:
constructor(props) {
super(props);
this.eventRefs = props.events.map(() => React.createRef());
this.scrollRef = React.createRef();
this.mapRef = React.createRef();
}
When I try to look at those same refs later:
componentDidUpdate(prevProps) {
// Even if I recreate the refs here
// All those values set above are equal to { current: null }
}
Turns out the solution is to not generate the refs in the constructor or update them in componentDidUpdate but to create the references before rending:
render() {
this._createRefs();
return ( /* ... */ );
}
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?
I've found some old code where they used to assign empty state in the constructor. Does this have any function or entire constructor can be deleted? Since there isn't any other code, I don't see any point having a constructor.
export default class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {};
}
render() {
return (<div title={this.state.title}></div>);
}
}
"Cannot read property 'title' of null"
this.state == null if you don't define it.
Therefore, applying a default value to state does make a difference if you want to safely reference any sub properties, without checking they exist.
The constructor can be safely deleted since you aren't initialising any state value and using it only after it has been initialised. For you it is still required as you use state in render. You only need to use constructor when you want to initialise certain state values or class variables. However with babel-plugin-transform-class-properties plugin, you can specify state as a class object itself like
export default class MyComponent extends React.Component{
state = { isVisible: true };
}
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();
}
Each time I'm dealing with internal component state in React I must first define empty object for state property or else I will get runtime errors throwing this.state is undefined
If I was about to do this:
render() {
const { someProperty } = this.state;
render <div>{someProperty}</div>
}
I'm gonna get error.
But the cure is quite simple:
constructor(props) {
super(props);
this.state = {};
}
However this is very annoying to do this over and over for each component that I create.
Is it somehow possible to force react add initial state as an empty object globally as to avoid all the boilerplate for empty state definition?
P.s. (maybe rhetorical question) Why isn't this done in the same React core?
Is it somehow possible to force react add initial state as an empty object globally as to avoid all the boilerplate for empty state definition?
You could create your own »component base class« extending from React.Component which implements this and then derive all of your components from this class instead.
import { React } from 'react';
class StatefulComponent extends React.Component {
constructor(...args) {
super(...args);
this.state = {};
}
}
export default StatefulComponent;
.
class MyComponent extends StatefulComponent {
render() {
const { something } = this.state;
return (<div>Hello, {something}</div>);
}
}
(Live Demo)
Typically extending components in React is discouraged as it's not idiomatic; and really no harm in adding this.state = {} in (every) component that requires state. But if you want to go that route, you can do it.
P.s. (maybe rhetorical question) Why isn't this done in the same React core?
Because many (or most) components don't require state, thus you'd be allocating wasted memory. This has been discussed in react#1851:
syranide: For components that don't need state (which could easily be the majority), avoiding an allocation is a win.
Destructuring values that are not an object, array, or iterable
When you try to use destructuring on null or undefined, you get a type error:
var {data} = null;
// TypeError: null has no properties
var {property} = this.state
// TypeError: state is not defined
So you could instead do
const { someProperty } = this.state || {};
However its even better to defined an initial state since its just one time.