Search code examples
javascriptreactjsapireact-lifecycle

Which ReactJS lifecycle function should be used when an api needs to called more than once due to a prop change?


I have a parent component called DeviceTab.js and a child component called Graph.js. The child component receives two props: name and span. I need to make api calls in Graph.js that are dependent on the initial name or span props and when they get updated from the parent. Right now, I make the api call in componentDidMount() shown below. However when the props get updated (and the states that I have for these two props also get updated), the api doesn't get called. I think it's because componentDidMount() gets only called once. Should I use another lifecycle method and/or move the api call somewhere else?

In Graph.js I convert these two props to states as such:

componentWillReceiveProps(props) {
    this.setState({ device: props.device })
    this.setState({ span: props.span })
  }

These two props are used as parameters in an api function call in componentDidMount() in my child component as such:

componentDidMount(){
   const {name, span} = this.state
   fetch('https://devices-api.com/name=${name}&span=${span}')
      .then((response) => response.json())
      .catch((error) => console.error("Error: ", error))
      .then((data) => {
        this.setState({ devices : data });
      });
}

When the parent props for name and span get updated in the parent component, I use componentDidUpdate() in the child component to update the child's name and span states as such:

componentDidUpdate(prevProps) {
    if (this.props.name !== prevProps.name) {
       this.setState({
        name: this.props.name,
       });
    }
    if (this.props.span !== prevProps.span) {
      this.setState({
       span: this.props.span,
      });
   }
  }

EDIT: I took @ Harmandeep Singh Kalsi 's advice and move the api call into componentDidUpdate but am still having issues with loading my data. Here's the updated method:

componentDidUpdate(prevProps){ 
    if (this.props.name !== prevProps.name) {
       this.setState({ name: this.props.name})
       // your api call goes here
       const {name, span} = this.state
       fetch('https://devices-api.com/name=${name}&span=${span}')
         .then((response) => response.json())
         .catch((error) => console.error("Error: ", error))
         .then((data) => {
         this.setState({ devices : data });
        });
       }
    }
    if (this.props.span !== prevProps.span) {
       this.setState({ span: this.props.span})
       // your api call goes here
       const {name, span} = this.state
       fetch('https://devices-api.com/name=${name}&span=${span}')
         .then((response) => response.json())
         .catch((error) => console.error("Error: ", error))
         .then((data) => {
         this.setState({ devices : data });
        });
       }
    }
} 

When I console.log the api url, I am seeing an infinite # of calls using the initial prop values passed from the parent i.e. https://devices-api.com/name=test1&span=2. When updated prop values are passed from the parent, there is another set of infinite api calls with the updated values i.e. https://devices-api.com/name=test2&span=4 . This prevents my component which uses the data from the api response from loading. My render method is as such:

if (this.state.devices.length > 0) {
      return (
        <Bar
          data={this.state.devices}
        />
      );
    } else {
      return (
        <div className="sweet-loading">
          <HashLoader css={override} size={150} color={"#42a315"} />
        </div>
      );
    }
  }

I just see nonstop Hashloader and half rendered graph switching back and forth.


Solution

  • After putting the api calls in the conditional in componentDidUpdate(prevProps). This issue was with the conditionals. I had to check the types for this.props.name vs prevProps.name. I used shallow-equal to check as such:

    componentDidUpdate(prevProps){
      if (!shallowEqualObjects(prevProps.name, this.props.name)) {
         // api call here
      }
    }
    

    For reference: https://www.npmjs.com/package/shallow-equal