I'm building a weather app, and I'm looking for some advice for best practices for updating state in a child ponent based on props sent from parent ponent AFTER an async call from parent ponent.
I have a parent ponent that makes an async/await call in the ponentDidMount()
method to the navigator.geolocation
and returns latitude and longitude which I want to send to the child ponent as props. Then, in the child ponent I need do an async/await call to the OpenWeatherMap API using the lat and long from the props. I then need to setState()
using the response
. I can't use ponentDidMount()
in the child because it mounts before the parent async/await call returns.
The problem is the application flow: The parent ponent mounts and renders, sending props to child as null
. The child ponent mounts and renders with null
props. Then, the async/await returns a response in parent, sets lat
and long
from response to state in the ponentDidMount()
, parent re-renders and sends props to child with correct values as lat
and long
. Child ponent updates with correct values in props. Now, at this point I need to setState()
with those props, however I obviously can't do it in ponentDidUpdate()
without re-rendering into a infinite loop.
So, what's a good way to acplish this task?
PARENT COMPONENT:
class Container extends React.Component {
state = {
cityState: {
city: "",
state: ""
},
latLong: {
lat: null,
long: null
}
};
ponentDidMount() {
this.getCityAndState();
}
getCityAndState() {
let latlng = "";
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async position => {
latlng = `${position.coords.latitude},${position.coords.longitude}`;
const response = await googleGeolocation.get(
`json?latlng=${latlng}&location_type=APPROXIMATE&result_type=locality&key=${APIkey}`
);
this.setState({
cityState: {
city: response.data.results[0].address_ponents[0].long_name,
state: response.data.results[0].address_ponents[2].short_name
},
latLong: {
lat: position.coords.latitude,
long: position.coords.longitude
}
});
});
}
}
render() {
return (
<div className="container">
<LocationTime location={this.state.cityState} />
<Forecast location={this.state.latLong} />
</div>
);
}
}
CHILD COMPONENT:
class Forecast extends React.Component {
state = {
today: {},
secondDay: {},
thirdDay: {},
fourthDay: {},
fifthDay: {}
};
async ponentDidUpdate() {
********** A bunch of logic in here to extract Weather API response
into 5 separate arrays which will each be sent to the <Day /> child ponents
after setting the state to those arrays(which doesn't work in this
life-cycle method, currently) **********
}
render() {
return (
<div className="forecast">
<Day forecast={this.state.today} />
<Day forecast={this.state.secondDay} />
<Day forecast={this.state.thirdDay} />
<Day forecast={this.state.fourthDay} />
<Day forecast={this.state.fifthDay} />
</div>
);
}
}
P.S. I always ask convoluted questions that end with fairly simple answers, lol
I'm building a weather app, and I'm looking for some advice for best practices for updating state in a child ponent based on props sent from parent ponent AFTER an async call from parent ponent.
I have a parent ponent that makes an async/await call in the ponentDidMount()
method to the navigator.geolocation
and returns latitude and longitude which I want to send to the child ponent as props. Then, in the child ponent I need do an async/await call to the OpenWeatherMap API using the lat and long from the props. I then need to setState()
using the response
. I can't use ponentDidMount()
in the child because it mounts before the parent async/await call returns.
The problem is the application flow: The parent ponent mounts and renders, sending props to child as null
. The child ponent mounts and renders with null
props. Then, the async/await returns a response in parent, sets lat
and long
from response to state in the ponentDidMount()
, parent re-renders and sends props to child with correct values as lat
and long
. Child ponent updates with correct values in props. Now, at this point I need to setState()
with those props, however I obviously can't do it in ponentDidUpdate()
without re-rendering into a infinite loop.
So, what's a good way to acplish this task?
PARENT COMPONENT:
class Container extends React.Component {
state = {
cityState: {
city: "",
state: ""
},
latLong: {
lat: null,
long: null
}
};
ponentDidMount() {
this.getCityAndState();
}
getCityAndState() {
let latlng = "";
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async position => {
latlng = `${position.coords.latitude},${position.coords.longitude}`;
const response = await googleGeolocation.get(
`json?latlng=${latlng}&location_type=APPROXIMATE&result_type=locality&key=${APIkey}`
);
this.setState({
cityState: {
city: response.data.results[0].address_ponents[0].long_name,
state: response.data.results[0].address_ponents[2].short_name
},
latLong: {
lat: position.coords.latitude,
long: position.coords.longitude
}
});
});
}
}
render() {
return (
<div className="container">
<LocationTime location={this.state.cityState} />
<Forecast location={this.state.latLong} />
</div>
);
}
}
CHILD COMPONENT:
class Forecast extends React.Component {
state = {
today: {},
secondDay: {},
thirdDay: {},
fourthDay: {},
fifthDay: {}
};
async ponentDidUpdate() {
********** A bunch of logic in here to extract Weather API response
into 5 separate arrays which will each be sent to the <Day /> child ponents
after setting the state to those arrays(which doesn't work in this
life-cycle method, currently) **********
}
render() {
return (
<div className="forecast">
<Day forecast={this.state.today} />
<Day forecast={this.state.secondDay} />
<Day forecast={this.state.thirdDay} />
<Day forecast={this.state.fourthDay} />
<Day forecast={this.state.fifthDay} />
</div>
);
}
}
P.S. I always ask convoluted questions that end with fairly simple answers, lol
You may use the ponentWillReceiveProps
lifecycle method.
The first child ponent render some props like lat
and lng
are null, then you can do something like:
async ponentWillReceiveProps(nextProps) {
if ( this.props.lat !== nextProps.lat || this.props.lng !== nextProps.lng ) {
const response = await YourAPICall(nextProps.lat, nextProps.lng)
this.setState(/* set your things here */)
}
}
Obviously this is just an outline...
Not sure why you use async/await instead of a normal fetch/axios call. In order to prevent entering in an infinite loop in your ponentDidUpdate
as you mentioned you need to run a conditional statement, something like:
ponentDidUpdate(prevState){
if (this.props.propertyYouWantToCheck !== prevState.propertyYouWantToCheck){
// set your state/logic here
}
}
Also you might want to consider to use fetch data only in the parent ponent and pass it down to the child ponent.