How to make React.js fetch data from api as state and pass this state data from its parent to child component

Viewed 4444

I am working on a React.js + D3.js project. I wanted App.js to fetch data from a json file and save this data into state and pass this parent sate data down to my child component state through the property. I found if I use static data in App.js works fine, but once fetching from a json file, it failed because no data can be stored into property. My App.js like this:

import React, { Component } from 'react';
import SandkeyGraph from './particle/SandkeyGraph';

class App extends Component {
  state = {
    data : null
  }

  // works fine in this way!
  // state = {
  //   data: {
  //     "nodes":[
  //     {"node":0,"name":"node0"},
  //     {"node":1,"name":"node1"},
  //     {"node":2,"name":"node2"},
  //     {"node":3,"name":"node3"},
  //     {"node":4,"name":"node4"}
  //     ],
  //     "links":[
  //     {"source":0,"target":2,"value":2},
  //     {"source":1,"target":2,"value":2},
  //     {"source":1,"target":3,"value":2},
  //     {"source":0,"target":4,"value":2},
  //     {"source":2,"target":3,"value":2},
  //     {"source":2,"target":4,"value":2},
  //     {"source":3,"target":4,"value":4}
  //     ]}
  // }

  componentWillMount() {
    this.getData('./data/sankey.json');
  }

  getData = (uri) => {
    fetch(uri)
    .then((response) => {
      return response.json();
    })
    .then((data) => {
      // successful got the data
      console.log(data);
      this.setState({ data });
   });
  }

  render() {
    // failed
    const { data } = this.state;
    return (
      <div>
        <SandkeyGraph
          height={300}
          width={700}
          id="d3-sankey" 
          sankeyData = {this.state.data} 
        />
      </div>
    );
  }
}

export default App;

parrt of my is like this:

class SankeyGraph extends Component {
  displayName: 'SankeyGraph';

  state = {
    sankeyData : null
  }

  constructor(props) {
    super(props);
    this.state.sankeyData = props.sankeyData || null;
  }

  PropTypes : {
    id : PropTypes.string,
    height: PropTypes.number,
    width: PropTypes.number,
    sankeyData : PropTypes.object,
  }

  componentDidMount () {
     // will be null, if using fetch from App.js
    //console.log(this.state.sankeyData);
    this.setContext();
  }
 //...

Does anyone know how to handle this situation? Thank you so much in advanced!

5 Answers

After working out the problem, it turned out that there was no problem with fetch. It just didn't account for null in any of the components in the program (It would crash after using a null value.

For example in render:

render() {
    if (this.state.data) {
      return (
        <div>
          <SandkeyGraph
            height={300}
            width={700}
            id="d3-sankey" 
            sankeyData = {this.state.data} 
          />
        </div>
      );
    }
    else {
      return <div/>
    }
}

Or, the use of a ternary operator would work as well to be more concise (answer by @Eliran):

return (
  {this.state.data ?
    <div>
      <SandkeyGraph
        height={300}
        width={700}
        id="d3-sankey" 
        sankeyData = {this.state.data} 
      />
    </div> : <div>No Data Available</div>
);

You can add in your render function a condition:

render() {
// failed
const { data } = this.state;
return (
  <div>
    {data ?
    <SandkeyGraph
      height={300}
      width={700}
      id="d3-sankey" 
      sankeyData={data} 
    /> : "Loading..."
    }
  </div>
);
}

and only if data is populated the component will be rendered.

...
class App extends React.Component {
constructor(props) {
super(props)

  this.state = {
    data : null
  }
}

It seems like an error on the state declaration?

1.- Import your json in App component on top like this: import jsonData from './data/sankey.json'

2.- Set jsonData in state jsonData in App component.

constructor(props) {
  super(props)
  this.state = {
    jsonData : {}
  }
}

componentWillMount() {
     this.setState({ jsonData })
  }

You do not need to fetch as you have your json locally.

Once you have your json in your state, you can display it in render like this for example:

this.state.jsonData.data.links.map(x=>
  <div>
   <div>x.links.source</div>
   <div>x.links.target</div>
  </div>
)

I've been testing and you need to replace the getData() method to this:

  getData = (uri) => {
    fetch(uri, {
      headers : { 
        'Content-Type': 'application/json',
        'Accept': 'application/json'
       }
    })
    .then((response) => {
      return response.json();
    })
    .then((data) => {
      // successful got the data
      console.log(data);
      this.setState({ data });
   });
  }

This is because you need to declare 'Content-Type' and 'Accept' headers on your fetch options in order to make your request.

Related