React Native - setState doesn't work in constructor - reactjs

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.

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.

Difference between componentWillMount and contructor in react? [duplicate]

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.

Unable to render content from api call in componentDidMount()

This is a simplified version of a problem I've faced while working in React. When I'm making a fetch call inside componentDidMount() and updating the state with the payload as follows:
componentDidMount(){
fetch("/api/endpoint").then(data => {
return data.json();
}).then(json => {
this.setState({
data: json
});
});
}
And rendering it out in render():
render(){
return(
<p>{this.state.data.title}</p>
)
}
I get an error saying this.state.data is undefined. I got around it by wrapping it around a conditional operator as follows:
{ this.state.data !== undefined ? <p>{this.state.data.title}</p> : null }
But my question is, if componentDidMount() fires before render() then how can this.state.data ever be undefined?
componentDidMount() runs after render(). You need to initialize the state correctly in the constructor
class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = { data: {title: "" } };
}
componentDidMount(){
fetch("/api/endpoint").then(data => {
return data.json();
}).then(json => {
this.setState({
data: json
});
});
}
render(){
return(
<p>{this.state.data.title}</p>
)
}
}
But my question is, if componentDidMount() fires before render() then how can this.state.data ever be undefined?
The order of componentDidMount firing before or after render doesn't really matter here. componentDidMount has async effects. Let's say it always fires before render, then what happens is this:
componentDidMount is run. It runs fetch. Running fetch doesn't make the browser wait for fetch to return and do setState. Instead fetch sends the request to the server, and returns a promise that will be fulfilled when the response from the server returns.
The code that runs when the promise is fulfilled is the function you pass as an argument to .then(). Since your setState call is inside .then(), it will only run once the response it available.
Meanwhile, your browser goes ahead and calls render. The response from the server may or may not have returned and resolved the promise (most probably it won't have returned because network is slower than the processor executing code). So render gets called with this.state.data not yet defined. So you need to consider a state where data is undefined in render.
But my question is, if componentDidMount() fires before render() then
how can this.state.data ever be undefined?
You assumption/understanding that componentDidMount lifecycle function fires before render is wrong, componentWillMount is fired before render while componentDidMount is fired after the render method. Now you might say that since componentWillMount is fired before render, I can move my fetch call in componentWillMount and thus I would not have to add a check in the render method, but that not right. Even though your request is fired before the render the response may not always be available before the render function is executed and since all of these happen asynchronously you need to add a check for the data in render method
render(){
return(
<p>{this.state.data? this.state.data.title: null}</p>
)
}
Check Use componentWillMount or componentDidMount lifecycle functions for async request in React question for more details

Reactjs: updating state immediately

Complete Reactjs newbie here. I know setState() is asynchronous. I know if I do setState() then the states are queued and batched, so I will not see the state changed immediately. Fair enough.
I have also read the following link and other questions in SO:
https://reactjs.org/docs/react-component.html#setstate
I can use: callback methods in setState, componentDidUpdate() lifecycle method, concat if the state is an array etc. I get all these ways. My problem is a simple one, also I am banging my head on this issue for past 2 days so I am literally at my wit's end now.
I have this logic:
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {showCompleted: false};
this.shouldShowFullList = false;
this.onShowCompleted = this.onShowCompleted.bind(this);
}
onShowCompleted(event) {
this.setState({showCompleted: event}, () => {this.shouldShowFullList = this.state.showCompleted;});
}
render() {
if (this.shouldShowFullList) {
return this.renderAllItemsIncludingCompleted();
} else {
return this.renderOnlyNotCompletedItems();
}
}
...
The logic is self-explanatory. My problem is even when I call this.shouldShowFullList in callback method of setState(), it still does not show an updated value. Value of this.shouldShowFullList is false when it should be true and vice versa. What is the best way to have the value of this.shouldShowFullList in lockstep with the this.state.showCompleted?
NOTE: onShowCompleted() is a callback method triggered from a child component. When a checkbox called "Show Completed" is checked, I should show a complete list of items, or else just the items which are not completed - something like ToDo list.
At onShowCompleted do
this.setState({ showCompleted: true })
or if you want toggle the value
this.setState({ showCompleted: !this.state.showCompleted })
Then in the render method you can do
if (this.state.showCompleted) {
return this.renderAllItemsIncludingCompleted();
} else {
return this.renderOnlyNotCompletedItems();
}
When you set a state using setState the method render is called with this.state updated. I think the callback (second argument) of this.setState is called after the render.
Because updating this.state make a new render it seems that react is pushing you to use this.state in the render method. In fact it's made for this usage. If you want to make a variable that have no purpose in the render you can use this.myVariable. Best pratice is to use only this.stateand this.props in the render (or function that depend of it).

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.

Resources