Is it possible to make initial state empty object in React? - reactjs

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.

Related

How can I force a component update with non-component class objects?

I have a React component with a non-component class object in its state. Right now, I am using the React component to display some of the other object's fields. I also want to make it so that every time the displayed field in the non-component class object changes, the react component is re-rendered to show that new updated field. How can I do this?
class ReactComponentClass extends React.Component {
constructor(props) {
super(props);
this.state = {
myObj: new SimpleJSClass()
}
}
render() {
return (
<div>
<p>Value: {this.state.myObj.value}</p>
</div>
);
}
}
class SimpleJSClass {
constructor() {
this.value = 1
}
}
I have heard of this.forceUpdate() but is that the only option? If it is possible, I would like to only update value instead of everything.
In React a component rerenders when its state changes. React will notice the change if the this.setState method is called.
You can create a class variable with the new instance
constructor(props) {
super(props);
this.simpleClassInstance = new SimpleJSClass();
this.state = {
myObj: this.simpleClassInstance
}
}
So when the class content upgraded call a setState with it.
this.setState({myObj: this.simpleClassInstance})
On the other hand I rarely use classes for data. I always use objects. Mutating the state is considered harmful. So calling setState with a new object when a data is changed is the common solution.

Function of assigning empty state in the react component constructor

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 };
}

React Attach State Directly to Instance

Why is it good practice to assign state variables to React's state? I'm a react beginner. I'm trying to understand the purpose of a separate state object. Naively, it seems just as good (and a little shorter), to write in a component:
this.foo = 3;
instead of:
this.state.foo = 3;
I understand that setState triggers a rerender, but it seems you could just easily set the state you want and then trigger a rerender directly. I've been using this.setState({}).
In all other cases, React appears to adhere to principles of simplicity. React tends to favor native JavaScript functionality over additional React-defined functionality except where additional functionality is absolutely required. Why did the creators of React go out of their way to give React state?
In-Depth Example
As a longer example, I can define a component Counter that increases its count every second. The first way uses react's state property. The second attaches its state directly to the instance.
Using state:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
setInterval((() => {
this.setState(previousState => ({
count: previousState.count + 1 }));
}), 1000);
}
render() {
return <h1>{this.state.count}</h1>;
}
}
Attaching properties directly to the instance:
class Counter extends React.Component {
constructor(props) {
super(props);
this.count = 0;
setInterval((() => {
this.count += 1;
this.setState({})
}), 1000);
}
render() {
return <h1>{this.count}</h1>;
}
}

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

React via ES6 classes. Why it work?

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.

Resources