React,父组件异步调用后使用props更新子组件状态

6
我正在构建一个天气应用程序,我正在寻求一些关于在子组件中根据父组件从异步调用发送的props更新状态的最佳实践建议。
我有一个父组件,在componentDidMount()方法中进行了异步/等待调用navigator.geolocation,并返回纬度和经度,我想将其作为props发送到子组件。然后,在子组件中,我需要使用来自props的lat和long对OpenWeatherMap API进行异步/等待调用。然后,我需要使用响应来setState()。我不能在子组件中使用componentDidMount(),因为它在父异步/等待调用返回之前挂载。
问题是应用程序流程:父组件挂载并呈现,将props作为null发送到子组件。子组件挂载并呈现具有null props。然后,异步/等待在父级返回响应,在componentDidMount()中将lat和long设置为状态,父级重新呈现并发送正确值的props作为lat和long到子级。子组件使用props中的正确值进行更新。现在,在这一点上,我需要使用这些props来setState(),但是我显然不能在componentDidUpdate()中执行它,否则会重新呈现成无限循环。
那么,完成此任务的好方法是什么?
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>
    );
  }
}

子组件:

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>
    );
  }
}

附言:我总是问一些复杂的问题,最终得到的答案却相当简单,哈哈。


0xc14m1z的回答很好,但通常认为在React中进行异步调用,然后将响应放入组件状态是不好的做法 - 这就是为什么大多数应用程序使用单独的全局状态(例如redux)的原因。请参见https://stackoverflow.com/a/45972353/5009210。 - Duncan Thacker
是的,我一开始试图避免集成Redux以保持简单性,但我想我应该重新构建它。 - rchap
2个回答

8
你可以使用 componentWillReceiveProps 生命周期方法。
第一个子组件渲染一些像 latlng 这样的 props 是 null,那么你可以这样做:
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 */)
  }
}

显然,这只是一个大纲...

componentWillReceiveProps 已经被弃用,请使用 componentDidUpdate 代替。 - Gaspar

3

不确定为什么要使用async/await而不是普通的fetch/axios调用。如您所述,为了防止进入无限循环,在您的componentDidUpdate中需要运行条件语句,例如:

componentDidUpdate(prevState){
 if (this.props.propertyYouWantToCheck !== prevState.propertyYouWantToCheck){
   // set your state/logic here
 }
}

此外,您可能希望考虑仅在父组件中使用fetch数据,并将其传递给子组件。


我使用axios调用API,但是在执行时,我认为使用async/await比fetch更好。 - rchap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接