Difference between componentWillMount and contructor in react? [duplicate] - reactjs

As far as I could see, the only thing a componentWillMount can do and a constructor cannot is to call setState.
componentWillMount() {
setState({ isLoaded: false });
}
Since we have not called render yet, a setState in componentWillMount will prepare the state object before we enter the first render() pass. Which is essentially the same thing a constructor does:
constructor(props) {
super(props);
this.state = { isLoaded: false };
}
But I see another use case where componentWillMount is useful (on server side).
Let's consider something asynchronous:
componentWillMount() {
myAsyncMethod(params, (result) => {
this.setState({ data: result });
})
}
Here we cannot use the constructor as assignment to this.state won't trigger render().
What about setState in componentWillMount? According to React docs:
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method will
not trigger a re-rendering. Avoid introducing any side-effects or
subscriptions in this method.
So, here I think React will use the new state value for the first render and avoids a re-render.
Question 1: Does this means, inside componentWillMount, if we call setState in an async method's callback (can be a promise callback), React blocks initial rendering until the callback is executed?
Having this setup on client-side (yes I see that use case in server-side rendering), if I assume the above is true, I will not see anything until my asynchronous method completes.
Am I missing any concepts?
Question 2: Are the any other use cases that I can achieve with componentWillMount only, but not using the constructor and componentDidMount?

Does this means, inside componentWillMount, if we call setState in an
async method's callback (can be a promise callback), React blocks
initial rendering until the callback is executed?
No, see here.
The following code doesn't block render (bear in mind this would be an anti pattern anyways to call setState there)
componentWillMount: function() {
new Promise((resolve, reject) => {
setTimeout(()=> {
resolve();
}, 2000)
}).then(() => this.setState({ promiseResult: 'World' }));
},
Question 2: Are the any other use cases that I can achieve with
componentWillMount only, but not using the constructor and
componentDidMount?
No, for ES6 classes you can discard componentWillMount. It is only needed if you use React.createClass({... })
EDIT: Apparently, I'm wrong. Thanks to #Swapnil for pointing this out. Here is the discussion.
React throws a warning if there is a side effect in the constructor which modifies state in another component, because it assumes that setState in the constructor itself and potentially during render() is being called. So no side effects in the constructor are desired.
This is not the case if you do it in componentWillMount, no errors are thrown. On the other hand, the guys from facebook discourage side effects in componentWillMount also. So if you don't have any side effects, you could use the constructor instead of componentWillMount. For side effects it is recommended to use componentDidMount instead of componentWillMount.
Either way, you don't need componentWillMount.

Related

How to use setState from .then in another function as a parameter in ReactJS

I am new to ReactJS. I have:
this.service
.getData({})
.then((response: any) => {
this.setState({
id: response.id
});
})
getStats(this.state.id) {
...
}
I am not able to get the value for id outside .then. I want to use id in multiple functions. How do I get the value in state so that I don't have to call the function several times? Please suggest.
on class base components, given the async nature of state update you use more often componentDidUpdate lifecycle. this lifecycle provides prevProps, prevState, snapshot as arguments, which are usefull to you do some validation with current state before triggering your actions.
fwiw, this.setState can receive a function as second parameter also. that function will be executed once state is updated, enabling you to have access to the most current state.
Once the state is updated, it will call the render method again. In the next render call you will have the state updated.
Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState.
So in order to use the state after the state is set, either you can use some effects or component life cycle to get the updated state.

React Native - setState doesn't work in constructor

Here is my code:
I want to set functions results which I get them as promises to some state variables but as it seems after use this.setState in then() function it returns empty object!
I checked in then() block and it contains data as it's supposed to.
class ProfileScreen extends Component{
constructor(props){
super(props);
this.state = {
profile_data: {},
user_data: {}
};
this._getUserData().then(response => {
this.setState({user_data: response});
});
//Empty Results
console.log(this.state.user_data);
this._getProfileData(this.state.user_data.id).then(response => {
this.setState({profile_data: response});
})
}
}
First of all you should never set your state in constructor using setState as a call to setState leads to rerendering of the view heirarchy which is not yet built to be rerendered.
A better place for doing so is componentDidMount because thats a lifecycle callback which ensures that the DOM is already rendered once and now you can update your state to see the changes in the view.
Coming to your problem, the empty result is due to the fact that the setState is asynchronous in nature, thus the console.log would have run even before the setState has updated the state.
You should rely on the second parameter of the setState which is a callback that runs post the state update.
this.setState({
user_data: response
}, () => {
console.log(this.state.user_data);
})
Don't do this in your constructor. Fetch your user detail in componentDidMount and then set your state as per your need
In addition to what Suraj said, if _getUserData is asynchronous, perhaps doing an AJAX call, then _getProfileData must be inside the .then callback too since you depend on the user_data to fetch the profile_data.
Try this snack out: https://snack.expo.io/#transfusion/stackoverflow-53694220
It might be cleaner to just chain the promises together.

Why in React componentWillReceiveprops fires before setState() in componentDidMount?

I have been programming with React for a while now but I have never faced this annoying issue, in one of my components componentWillReceiveProps fires before setState() in componentDidMount gets executed. This causes several issues in my application.
I have a variable this.props.flag received from props which is going to be stored in the state of the component:
componentDidMount() {
if (!_.isEmpty(this.props.flag)) {
console.log('Flag Did:', this.props.flag);
this.setState({
flag: this.props.flag
},
() => doSomething()
);
}
In my componentWillReceiveProps method the variable this.state.flag is going to be replaced just if it is empty or if it different from the value of this.props.flag (the checks are made by using the lodash library):
componentWillReceiveProps(nextProps) {
const { flag } = this.state;
console.log('Flag Will:', !_.isEqual(flag, nextProps.flag), flag, nextProps.flag);
if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
Suppose that the prop flag in this case has always the same value and that this.state.flag is initialized to undefined. When I check the console log I see the following result:
Flag Did: true
Flag Will: true undefined true
Therefore when the code enters componentWillReceiveProps the value of this.state.flagis still undefined, that means has not been set yet by the setState in componentDidMount.
This is not consistent with React lifecycle or am I missing something? How can I avoid such behaviour?
ComponentWillReceiveProps() will be called in each update life-cycle caused by changes to props (parent component re-rendering). Since Javascript is synchronous you might have validate props sometimes to save app crashes. I've not totally understood the context of your app but what you can do is:
componentWillReceiveProps(nextProps) {
const { flag } = this.state;
if(!flag){
return;,
}
console.log('Flag Will:', !_.isEqual(flag, nextProps.flag), flag, nextProps.flag);
if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
You can return if state is undefined. It will be called again upon parent re-rendering. But this might not be use-case.
Anyways you should look into this:
But I can think of at least 1 (maybe theoretical) scenario where the order will reversed:
Component receives props, and starts rendering. While component is
rendering, but has not yet finished rendering, component receives new
props. componentWillReceiveProps() is fired, (but componentDidMount
has not yet fired) After all children and component itself have
finished rendering, componentDidMount() will fire. So
componentDidMount() is not a good place to initialise
component-variables like your { foo: 'bar' }. componentWillMount()
would be a better lifecycle event. However, I would discourage any use
of component-wide variables inside react components, and stick to
design principles:
all component variables should live in either state or props (and be
immutable) all other variables are bound by the lifecycle method (and
not beyond that)
As suggested by user JJJ, given the asynchronous nature of setState, the check if (!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) in componentWillReceiveProps is executed before setState inside componentDidMount executes flag: this.props.flag. The order of the operations is:
Code enters componentDidMount.
Code executes setState in
componentDidMount (flag: this.props.flag hasn't happened yet).
Code exits componentDidMount, setState in componentDidMount is
still under execution (flag: this.props.flag hasn't happened yet).
Component receive new props, therefore enters
componentWillReceiveProps.
The statement if
(!_.isEmpty(nextProps.flag) && !_.isEqual(flag, nextProps.flag)) in
componentWillReceiveProps is executed (this.state.flag is still
undefined).
Code finishes the execution of setState inside
componentDidMount and sets flag: this.props.flag and executes
doSomething().
Code finishes the execution of setState inside
componentWillMount and sets flag: nextProps.flag and executes
doSomething().
Given the asynchronous nature of setState, 6 and 7 could be executed in parallel and therefore we do not know which one will finish its execution first. DoSomething() in this case is potentially called at least 2 times when it must be called once instead.
In order to solve these issues, I changed my code this way:
componentWillReceiveProps(nextProps) {
if (!_.isEmpty(nextProps.flag) && !_.isEqual(this.props.flag, nextProps.flag)) {
this.setState({
flag: nextProps.flag,
},
() => doSomething()
);
}
}
This way I compare the new version(nextProps) with the old version(this.props) of the props, without waiting for the flag value to be stored in the component's state.

this.setState() does not work in componentWillReceiveProps

I have a login page in which I am using componentWillReceiveProps to route to the next page. But the state that I am setting inside componentWillReceiveProps does not seem to set.
This is my componentWillReceiveProps method :
componentWillReceiveProps(nextProps) {
if (nextProps.isAuthenticated === true) {
browserHistory.push('/home');
} else {
console.log("this.props :::" + JSON.stringify(this.props))
console.log("this.state :::" + JSON.stringify(this.state))
console.log("nextProps :::" + JSON.stringify(nextProps))
this.setState({
errorMessage: nextProps.authenticationError
})
console.log("this.state :::" + JSON.stringify(this.state))
}
}
The console output I am getting is this :
this.props :::{"authenticationError":null}
this.state :::{"username":"35135","password":"3135","errorMessage":""}
nextProps :::{"isAuthenticated":false,"authenticationError":"Could not find user in DB."}
this.state :::{"username":"35135","password":"3135","errorMessage":""}
Here even after setting the state, my state has not changed.
Please tell me what is it that I'm doing wrong.
EDIT: I have this component which is ErrorText , which takes in the errroMessage property.
<ErrorText errorMsg={this.state.errorMessage}></ErrorText>
setState() is an asynchronous operation, so it doesn't take effect immediately:
setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state [...]
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback [...], either of which are guaranteed to fire after the update has been applied.
Here's an example of a setState callback in your context:
this.setState(
{ errorMessage: nextProps.authenticationError },
function() {
console.log( 'this.state ::: ' + JSON.stringify( this.state ) );
}
);
Refer to same stackoverflow question:
Why Calling react setState method doesnt mutate the state immediately
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
This is likely because this.setState is an async function. You can pass a callback to handle events which should happen directly after setting state. Checkout this chosen answer for a code example.

React JS - is there a shorthand for this.setState(this.state)?

Is there a shorthand syntax in React JS which could perform this.setState(this.state) ? What are the implications of calling forceUpdate() instead of this.setState(this.state) after modifying the state?
Thanks!
assume you have a sideDrawer, your state is :
state = {
showSideDrawer: false
};
when you click on the toggle icon, the bottom function will run:
sideDrawerToggleHandler = () => {
this.setState((prevState) => {
return {showSideDrawer: !prevState.shorthand}
});
}
this was an example, I hope that will be useful
According to React documentation, the state should not be mutated directly.
Notes:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.

Resources