如何将状态从父组件传递给子组件以在API调用中使用?

时间:2018-07-22 05:35:01

标签: reactjs axios

我有一个React应用,可以查询我在Spring Boot中构建的API。 React端应该查询数据库以找到许多Activity对象来填充一个Itinerary对象。我想跟踪活动的ID,因此该应用不会多次返回相同的ID。这是我的设置方式:

  1. 行程组件在其状态下具有一个名为“活动”的数组,该数组将保存ID。开始时为空。行程组件还具有一个名为UpdateActivities()的函数,当添加活动时,该函数会使用新的ID更新状态。

  2. UpdateActivities作为回调传递给子组件Activity作为道具。状态中的活动数组将转换为字符串(因为我将在API调用中使用它),并且还作为道具传递给Activity组件。

  3. Activity组件中,回调函数作为道具进一步传递给ActivityDetails组件,并且ID字符串也进一步传递给ActivityDetails。 / p>

  4. ActivityDetails组件对Spring Boot应用程序进行实际的API调用,并使用回调函数更新父组件上的State。我试图将ID字符串保存为一个常量,以便在我的API调用中使用。我希望字符串在通话结束时显示,因此我的应用程序将知道跳过那些具有这些ID(当前已硬编码为1)的活动。

如果我将1替换为常量ID字符串,则API调用将不起作用,因为它显示为空。这是异步问题吗?有没有一种方法可以使React应用程序在父组件上的State更新之前不对ActivityDetails进行API调用?

  

行程组成部分

class Itinerary extends Component {
  constructor(props) {
    super(props);
    this.state = {
      template: null,
      activities: []
    };
  }

  updateActivities = (id) => {
    const activityList = [...this.state.activities, id];
    this.setState({
      activities: activityList
    })
  }

  componentDidMount = () => {
    const {duration, travelerType, pace} = this.props.userAnswers;
    const transport = this.props.userAnswers.internalTravel.sort().join(', ');
    const TEMPLATES_URL = `http://localhost:8080/templates/duration/${duration}/travelers/${travelerType}/pace/${pace}/transport/${transport}`;
    axios.get(TEMPLATES_URL)
      .then(response => {
        this.setState({
          template: response.data[0].content
        });
      })
      .catch(function (error) {
        console.log(error.message);
      });
  }

  render() {
    let cities;
    if (this.state.template === null) {
      cities = <div><h3>Loading...</h3></div>;
    } else {
      let data = this.state.template.split(", ");
      cities = data.map((city, index) => {
        return (
          <section>

            <City
              key={index}
              day={index + 1}
              city={city}
            />
            <Activity
              key={`${city}${index}`}
              day={index + 1}
              lastDay={data.length}
              city={city}
              userAnswers={this.props.userAnswers}
              updateActivityState={this.updateActivities}
              activityList={this.state.activities.join(', ')}
            />
          </section>
        )
      });
    }


    return (
      <div>    

        <div className="row">

          <div className="col s9">
            <h3>{cities}</h3>
          </div>

        </div>

      </div>
    );
  }
}

export default Itinerary;
  

活动组件

class Activity extends Component {

  render() {
    let city = this.props.city;

    if(this.props.day === 1) {
      return (
        <ActivityDetails
          userAnswers={this.props.userAnswers}
          city={city}
          timeOfDay="evening"
          handleActivityList={this.props.updateActivityState}
          activities={this.props.activityList}/>

      );
    } else if (this.props.day === this.props.lastDay) {
      return (
        <ActivityDetails
          userAnswers={this.props.userAnswers}
          city={city} timeOfDay="morning"
          handleActivityList={this.props.updateActivityState}
          activities={this.props.activityList} />
      );
    } else {
      return(
        <section>
          <ActivityDetails
            userAnswers={this.props.userAnswers}
            city={city}
            timeOfDay="morning"
            handleActivityList={this.props.updateActivityState}
            activities={this.props.activityList} />
          <ActivityDetails
            userAnswers={this.props.userAnswers}
            city={city}
            timeOfDay="afternoon"
            handleActivityList={this.props.updateActivityState}
            activities={this.props.activityList} />
          <ActivityDetails
            userAnswers={this.props.userAnswers}
            city={city}
            timeOfDay="evening"
            handleActivityList={this.props.updateActivityState}
            activities={this.props.activityList} />
        </section>
      );
    }
  }
}

export default Activity;
  

ActivityDetails组件

class ActivityDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activity: 'Loading...',
    };
  }

  componentDidMount() {

    const {travelParty, budget, pace, sites} = this.props.userAnswers;
    const cityTravel = this.props.userAnswers.cityTravel.sort().join(', ');
    const interests = this.props.userAnswers.interests.sort().join(', ');
    const entertainment = this.props.userAnswers.entertainment.sort().join(', ');

    const currentActivities = this.props.activities;
    console.log(`currentActivities: ${currentActivities}`);
    const city = this.props.city;
    const timeOfDay = this.props.timeOfDay;

    const ACTIVITY_URL = `http://localhost:8080/filter/${city}/${timeOfDay}/${travelParty}/${budget}/${pace}/${sites}/${cityTravel}/${interests}/${entertainment}/1`;
    console.log(ACTIVITY_URL);
    axios.get(ACTIVITY_URL)
      .then(response => {
        const newActivity = response.data.content;
        const updatedActivityId = response.data.id;
        this.props.handleActivityList(updatedActivityId);

        this.setState({
          activity: newActivity,
        });
      })
      .catch(function(error) {
        console.log(error.message);
      });
    }
  render () {
    return (
      <div>{this.state.activity}</div>
    );
  }

}


export default ActivityDetails;

使用ComponentDidMount进行了修订:

class ActivityDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activity: 'Loading...',
    };
  }

  getActivity() {
    const {travelParty, budget, pace, sites} = this.props.userAnswers;
    const cityTravel = this.props.userAnswers.cityTravel.sort().join(', ');
    const interests = this.props.userAnswers.interests.sort().join(', ');
    const entertainment = this.props.userAnswers.entertainment.sort().join(', ');

    const city = this.props.city;
    const timeOfDay = this.props.timeOfDay;

    const currentActivities = this.props.activities;
    console.log(`currentActivities: ${currentActivities}`);

    const ACTIVITY_URL = `http://localhost:8080/filter/${city}/${timeOfDay}/${travelParty}/${budget}/${pace}/${sites}/${cityTravel}/${interests}/${entertainment}/${currentActivities}`;
    console.log(ACTIVITY_URL);

    axios.get(ACTIVITY_URL)
    .then(response => {
      const newActivity = response.data.content;
      const updatedActivityId = response.data.id;
      this.props.handleActivityList(updatedActivityId);

      this.setState({activity: newActivity});
    })
    .catch(function(error) {
      console.log(error.message);
    });
  }

  componentDidMount() {
    this.getActivity();
  }

  componentDidUpdate(prevProps) {
    if(prevProps.activities !== this.props.activities) {
      this.getActivity();
    }
  }


  render () {
    return (
      <div>{this.state.activity}</div>
    );
  }

}


export default ActivityDetails;

1 个答案:

答案 0 :(得分:0)

  

如果我将1替换为常量ID字符串,则API调用将不起作用,因为它显示为空。这是异步问题吗?

是的,是的。 Itinerary的获取可能与ActivityDetails的获取大致同时进行,因此state.activities仍然有一个空数组(对它进行了突变后,请输入错误的空字符串然后传下来)。

  

有没有一种方法可以使React应用程序在父组件上的State更新之前不对ActivityDetails进行API调用?

是的,您可以使用其他生命周期方法来简化此过程。在您的情况下,您可能想要componentDidUpdate

您可以执行以下操作:

class ActivityDetails extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activity: 'Loading...',
    };
  }

  doMyGet(values) {
    const ACTIVITY_URL = `http://localhost:8080/filter/${city}/${timeOfDay}/${travelParty}/${budget}/${pace}/${sites}/${cityTravel}/${interests}/${entertainment}/1`;
    console.log(ACTIVITY_URL);
    axios.get(ACTIVITY_URL)
      .then(response => {
        const newActivity = response.data.content;
        const updatedActivityId = response.data.id;
        this.props.handleActivityList(updatedActivityId);

        this.setState({
          activity: newActivity,
        });
      })
      .catch(function(error) {
        console.log(error.message);
      });
  }

  componentDidMount() {

    const {travelParty, budget, pace, sites} = this.props.userAnswers;
    const cityTravel = this.props.userAnswers.cityTravel.sort().join(', ');
    const interests = this.props.userAnswers.interests.sort().join(', ');
    const entertainment = this.props.userAnswers.entertainment.sort().join(', ');

    const currentActivities = this.props.activities;
    console.log(`currentActivities: ${currentActivities}`);
    const city = this.props.city;
    const timeOfDay = this.props.timeOfDay;

    this.doMyGet(currentActivities)
  }

  componentDidUpdate(prevProps) {
    if (this.props.activities !== prevProps.activities) {
      this.doMyGet(this.props.activities)
    }
  }
  
  render () {
    return (
      <div>{this.state.activity}</div>
    );
  }

}


export default ActivityDetails;