最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React, updating state in child component using props after async call in parent component - Stack Overflow

programmeradmin6浏览0评论

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

Share Improve this question asked Feb 2, 2019 at 23:18 rchaprchap 1111 silver badge9 bronze badges 2
  • 0xc14m1z's answer is good, but in general it's considered bad React practice to do async calls and then put the response in to ponent state - this is why most apps use a separate global state (e.g. redux). See stackoverflow./a/45972353/5009210 – Duncan Thacker Commented Feb 2, 2019 at 23:43
  • Yeah, I was trying to avoid building-in Redux, to try to keep it simpler, but I guess I should refactor it in. – rchap Commented Feb 3, 2019 at 3:15
Add a ment  | 

2 Answers 2

Reset to default 8

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.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论