Fetch data on Async ComponentDidMount before rendering - reactjs

In my componentWillMount function, I have an async function getResults that fetches data from a server. I want to pass the response from getResults to the same component on initial load, but I noticed that my component renders before waiting for the data to come back from getResults. How do I make the component wait for the response before having it render? I am using Redux to handle my state.
async componentWillMount() {
const response = await getResults(this.props.category)
}

Async/Await only makes your code look synchronous.
async componentWillMount() {
const response = await getResults(this.props.category)
this.setState({
// use response to set your state.
});
}
Set state is what causes the re-render.
You then use the state to determine if you are in a loading state. If so it may be good to show the user. You also want to handle the default data in the first render. Once you retrieve your response you set the data to the second state.

It's idiomatic React to render your component asynchronously. You'll fetch your data, and set your state accordingly... with a new state, your component will re-render. A common practice it to render the component with a spinner, and when the state is set, re-render with the actual data. At the very least, you can render null until your async request completes and sets your state.

componentWillMount() is invoked just before mounting occurs. It is
called before render(), therefore calling setState() synchronously in
this method will not trigger an extra rendering. Generally, we
recommend using the constructor() instead.
Avoid introducing any side-effects or subscriptions in this method.
For those use cases, use componentDidMount() instead.
This is the only lifecycle hook called on server rendering.
source
You can use componentDidMount() and make the rendering conditional and waiting for the result from the API, and then render it with the result.

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.

How do I use async await when a component leaves view

How do I use Async Await ?
When this.props.onChangeStep1() is called, the component where the view is rendered is replaced by another component.
If the component has been swapped would Async Await still work ?
s3.upload(params, function(err, data) {
this.props.onChangeStep1(); //<----- After s3.upload I want to call this first
this.setState( //<----But I want this to run in the background
// even though the component is not in view
{
myState: "data-from-s3Upload"
});
}.bind(this)
);
If a callback is set to be executed later and the component that initiated the asynchronous call is unmounted, the callback will be executed regardless. If the callback attempts to act on the unmounted component (changing its state for instance) it will be considered as a memory leak and React will notify you with an error message in the console.
this.props.onChangeStep1(e) // <-- Forward data and Process logic on App.js / Parent Component

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.

API calls in componentDidUpdate and componentWillReceiveProps

I am making API calls in the componentDidMount() method as recommended by React.
Inside the component I have a <Link> which points to the same component with different URL params.
However when I click on the <Link> the componentDidMount() is not called and I can't make API calls with updated URL params.
So I tried putting the API calls inside componentDidUpdate() and componentWillReceiveProps()
This results in stack overflow as the API calls set state triggering componentDidUpdate() and componentWillReceiveProps() which again make the API calls leading to infinite loop.
How do I overcome this?
What is the recommended solution which fits into React philosophy?
Inside the component I have a which points to the same component with different URL params
This means the component is not unmounted/remounted as it's the same component, so componentDidMount will not be called.
API calls set state triggering componentDidUpdate() and componentWillReceiveProps() which again make the API calls leading to infinite loop
You should make the API call in componentDidUpdate (componentWillReceiveProps will be deprecated and async calls should not be executed here anyway), but check if it's already been executed by checking if the data returned by the API call is there or changed, to avoid infinite re-rendering:
componentDidUpdate(prevProps, prevState) {
if (this.state.data.length === 0) { // or compare with `prevState`
// make the api call
}
}
The componentDidUpdate() receives previous props by using it you can if the props are updated or not... if updated then call API. In my project I am doing it as follows:
componentDidUpdate(prevProps) {
if (prevProps.match.params.category !== this.props.match.params.category) {
this.getBlogList(this.props.match.params.category);
}
}
remove componentDidUpdate and use only componentWillReceiveProps(){ <--- and call api here}

`componentWillReceiveProps` explanation

I recently wanted to upgrade my knowledge of React, so I started from the component lifecycle methods. The first thing that got me curious, is this componentWillReceiveProps. So, the docs are saying that it's fired when component is receiving new (not necessarily updated) props. Inside that method we can compare them and save into the state if needed.
My question is: Why do we need that method, if changes in props of that component (inside parent render) will trigger the re-render of this child component?
One common use case are state (this.state) updates that may be necessary in response to the updated props.
Since you should not try to update the component's state via this.setState() in the render function, this needs to happen in componentWillReceiveProps.
Additionally, if some prop is used as a parameter to some fetch function you should watch this prop in componentWillReceiveProps to re-fetch data using the new parameter.
Usually componentDidMount is used as a place where you trigger a method to fetch some data. But if your container, for example, UserData is not unmounted and you change userId prop, the container needs to fetch data of a user for corresponding userId.
class UserData extends Component {
componentDidMount() {
this.props.getUser(this.props.userId);
}
componentWillReceiveProps(nextProps) {
if (this.props.userId !== nextProps.userid) {
this.props.getUser(nextProps.userId);
}
}
render() {
if (this.props.loading) {
return <div>Loading...</div>
}
return <div>{this.user.firstName}</div>
}
}
It is not a full working example. Let's imagine that getUser dispatch Redux action and Redux assign to the component user, loading and getUser props.
It 'serves' as an opportunity to react to the incoming props to set the state of your application before render. If your call setState after render you will re-render infinitely and that's why you're not allowed to do that, so you can use componentWillReceiveProps instead.
But... you are beyond CORRECT in your confusion, so correct in fact that they are deprecating it and other Will-lifecycle hooks Discussion Deprecation.
There are other ways to accomplish what you want to do without most of those Will-lifecycle methods, one way being don't call setState after render, just use the new incoming props directly in render (or wherever) to create the stateful value you need and then just use the new value directly, you can then later set state to keep a reference for the next iteration ex: this.state.someState = someValue, this will accomplish everything and not re-render the component in an infinite loop.
Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.
Look at this article
the componentWillReceiveProps will always receive as param "NxtProps", componentWillReceiveProps is called after render().
some people use this method use this to compare nxtProps and this.props to check, if something should happen before the component call render, and to some validations.
check the react's documentation to know more about react lifecycle!
hope this could help you!
changes in props of that component (inside parent render) will trigger the re-render of this child component
You are absolutely right. You only need to use this method if you need to react to those changes. For instance, you might have a piece of state in a child component that is calculated using multiple props.
Small Example:
class Test extends Component {
state = {
modified: "blank"
};
componentDidMount(){
this.setState({
modified: `${this.props.myProp} isModified`
});
}
componentWillReceiveProps(nextProps) {
this.setState({
modified: `${nextProps.myProp} isModified`
});
}
render() {
return <div className="displayed">{this.state.modified}</div>
}
}
In this example, componentDidMount sets the state using this.props. When this component receives new props, without componentWillReceiveProps, this.state.modified would never be updated again.
Of course, you could just do {this.props.myProp + "IsModified"} in the render method, but componentWillReceiveProps is useful when you need to update this.state on prop changes.

Resources