Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React, updating state in child component using props after async call in parent component

I'm building a weather app, and I'm looking for some advice for best practices for updating state in a child component based on props sent from parent component AFTER an async call from parent component.

I have a parent component that makes an async/await call in the componentDidMount() method to the navigator.geolocation and returns latitude and longitude which I want to send to the child component as props. Then, in the child component 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 componentDidMount() in the child because it mounts before the parent async/await call returns.

The problem is the application flow: The parent component mounts and renders, sending props to child as null. The child component 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 componentDidMount(), parent re-renders and sends props to child with correct values as lat and long. Child component 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 componentDidUpdate() without re-rendering into a infinite loop.

So, what's a good way to accomplish this task?

PARENT COMPONENT:

class Container extends React.Component {
  state = {
    cityState: {
      city: "",
      state: ""
    },
    latLong: {
      lat: null,
      long: null
    }
  };

  componentDidMount() {
    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_components[0].long_name,
            state: response.data.results[0].address_components[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 componentDidUpdate() {
    ********** A bunch of logic in here to extract Weather API response 
into 5 separate arrays which will each be sent to the <Day /> child components
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

like image 801
rchap Avatar asked Feb 02 '19 23:02

rchap


People also ask

How do I update the prop in a child component?

Solution : As I had mentioned earlier — there are no direct ways to mutate a prop directly in child components. However when we pass the props from parent to child, we can also pass the function(with the logic) which changes the props.

How can you update the state from the child component?

To update the parent state from the children component, either we can use additional dependencies like Redux or we can use this simple method of passing the state of the parent to the children and handling it accordingly.

Can I pass props to a child component React?

React components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions.


2 Answers

You may use the componentWillReceiveProps lifecycle method.

The first child component render some props like lat and lng are null, then you can do something like:

async componentWillReceiveProps(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...

like image 60
0xc14m1z Avatar answered Oct 30 '22 07:10

0xc14m1z


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 componentDidUpdate as you mentioned you need to run a conditional statement, something like:

componentDidUpdate(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 component and pass it down to the child component.

like image 37
Kleo Avatar answered Oct 30 '22 05:10

Kleo