4

I have a react code as below which basically creates a treemap:

class ChartModal extends Component{
   constructor(props){
      super(props)
}
callApi(){

  fetch(someurl)
  .then((result) => {

    return result.json();
  }).then((jsonResult) => {
    console.log(jsonResult);
  })
}
render(){
  return(
    <Modal
            onOk={() => this.props.toggleVisibility()}
            onCancel={() => this.props.toggleVisibility()}
            visible={this.props.isVisible}
            okText={'ok'}
            cancelText={'cancel'}
            confirmLoading={false}
            title="Intent distribution chart"
        >
            <h1>HOWDY</h1>
            <TreeMap
                data =  {this.callApi()}//this is where i want the data returned by apicall
                width={400}
                valueUnit={'count'}
            />
        </Modal>
    )
  }
}

Now I want the data returned by api call to be used inside the tree map component, but the way I am doing it now doesn't seem to be working. When I run this, the data in the tree map comes out to be null though I expect a json returned by api call to be there.

Angel Cuenca
  • 1,637
  • 6
  • 24
  • 46
Srijan Sharma
  • 683
  • 1
  • 9
  • 19
  • What do you mean it isn't working? Please be more specific. Is it throwing an error? If so, what error? Giving undesired results? What are the desired results and what does this return? – Antimony Nov 15 '17 at 19:29
  • @Antimony when i run this, the data in treemap is null. – Srijan Sharma Nov 15 '17 at 19:33

2 Answers2

5

You need to invoke the API in componentWillMount or any other life cycle hooks and in your API callback use setState to ensure that the result is set to the components state variable. That way after the setState your component can access the value resolved by your API call.

NOTE: this.state.result will be null during the first render. Make sure to have a null check in the TreeMap component before accessing this.props.data.

IMP NOTE

Great suggestion from @aram90 about the setState invocation on unmounted components. To avoid this this.canSetState, instance variable is defined by me and added to prevent any possible setState invocation on unmounted components.

I used this approach just to call the API as early as possible even if it means a nanosecond. However, another approach would be to call the API in componentDidMount and then check for this._ismounted instance variable accordingly. For more on this checkout the answer here Is there a way to check if the react component is unmounted?. React docs suggests not to use isMounted().

There are many ways to achieve this but this is more React kind of way.

class ChartModal extends Component{
  constructor(props){
    super(props)

    this.state = {
      result: null
    }
    this.canSetState = false;
  }

  componentWillMount() {
    this.canSetState = true;
    this.callApi();
  }

  componentWillUnmount() {
    this.canSetState = false;
  }

  callApi(){

    fetch(someurl)
    .then((result) => {

      return result.json();
    }).then((jsonResult) => {
      this.canSetState && this.setState({result: jsonResult})
    })
  }

  render(){
    return (
      <Modal
        onOk={() => this.props.toggleVisibility()}
        onCancel={() => this.props.toggleVisibility()}
        visible={this.props.isVisible}
        okText={'ok'}
        cancelText={'cancel'}
        confirmLoading={false}
        title="Intent distribution chart"
      >
        <h1>HOWDY</h1>
        <TreeMap
          data={this.state.result}
          width={400}
          valueUnit={'count'}
        />
      </Modal>
    )
  }
}
Nandu Kalidindi
  • 6,075
  • 1
  • 23
  • 36
  • Actually this approach can potentially result in an error when fetching the URL is completed after component is unmounted. React will throw an error when trying to call `setState` on an unmounted component. A simple way of fixing it is to have a check if component is still mounted or not before setting the state after an async function. And also it is better to call the API from `componentDidMount` instead so that `isMounted` check will not fail if the call returns too fast before the component is actually mounted. – artahian Nov 15 '17 at 21:03
  • That is in fact a great catch. @aram90 Could you please check if this is something that is similar. I just prefer issuing the API call as early as possible. Let me know if this could work as well? – Nandu Kalidindi Nov 15 '17 at 21:46
  • Kalindindi your approach is also fine, even though checking before the component is mounted is not necessary since you can setState even before it is mounted. However, React documentation says: "componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request." https://reactjs.org/docs/react-component.html#componentdidmount – artahian Nov 16 '17 at 01:15
2

The problem is that callApi isn't returning anything. If it's supposed to assign the data somewhere, it isn't. And if does return something, it will be a promise since it's async.

What you can do is use a state variable and map it to the data prop of your TreeMap. Then use the componentDidMount lifecycle function to fetch the data once the component is rendered. Once it responds, update the mapped state variable with the results. In theory, it should re-render TreeMap automatically.

Joseph
  • 117,725
  • 30
  • 181
  • 234