0

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.

bwang
  • 49
  • 7

2 Answers2

2

You should use componentDidUpdate instead For details check this link : https://reactjs.org/docs/react-component.html#componentdidupdate

Example:

componentDidUpdate(prevProps){ 
   if(currentProps !== previProps){
     <your api call goes here>
   }
} 
Harmandeep Singh Kalsi
  • 3,315
  • 2
  • 14
  • 26
  • I moved the api call into `componentDidUpdate()` as you described. However in my api call, I use `setState()` to set my response data as a state. Although I am able to make an api call with the updated props that were passed from the parent, this is causing an infinite re-rendering of the child component and the api is called many times. Is there a way around this? – bwang Aug 16 '20 at 04:59
  • Could you please add the code , how you did that ? Without seeing code it is difficult to answer . If possible can you provide the link to sandbox with the code? – Harmandeep Singh Kalsi Aug 16 '20 at 05:50
  • Sorry about the vague response I updated the original post with what I did. Thanks for your time Harmandeep! – bwang Aug 16 '20 at 06:13
  • The API call is happening , even if the props change or not . It is not wrapped in the if where you are comparing the prevProps to current props. Please read the documentation again https://reactjs.org/docs/react-component.html#componentdidupdate – Harmandeep Singh Kalsi Aug 16 '20 at 07:25
  • Apologies, I updated my code where the api call is wrapped in both the conditionals. Original post is updated. Same thing still happening. – bwang Aug 16 '20 at 09:01
  • Can you add the code in sandbox and share the link here? – Harmandeep Singh Kalsi Aug 16 '20 at 09:09
  • https://codesandbox.io/s/competent-shadow-vu1r4 I put the general structure of both parent and child components here. I am unable to provide the exact api and thus the responses from the api's are empty. Not sure if it's possible to identify if it's a structural/logical error. The issue is that when the device dropdown and span dropdown is selected, there is an infinite api call with the updated values in Child.js. And the graph is unable to render properly. Many thanks! – bwang Aug 17 '20 at 02:33
0

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

bwang
  • 49
  • 7