Reputation: 37
I have these two components: I fetch the data in the parent component and then pass it to the child component, which formats it into an object so that it can be used in the construction of a graph.
When the child component is mounted for the first time, the graph is not rendered. If I try to change the child component code to update it (e.g. delete linkColor from ForceGraph2D’s props), the graph will be displayed correctly. I want to find a way to see it right away, the first time the component is mounted.
Parent component:
import React, {Fragment, useEffect, useState} from "react";
import AddressList from "./AddressList";
import ClusterGraph from "./ClusterGraph";
import axios from "axios";
function ClusterPage(props) {
const [cluster, setCluster] = useState([]);
const [subCluster, setSubCluster] = useState([]);
useEffect(() => {
fetchData1();
fetchData2();
},[]);
const fetchData1 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get('http://localhost:5000/', {
params: {address: address}
});
const data = await response.data;
setCluster(data);
}
const fetchData2 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get('http://localhost:5000/sub/', {
params: {address: address}
});
const data = await response.data;
setSubCluster(data);
}
return (
<div key={props.match.params.id}>
<Fragment>
<AddressList data={cluster} />
<ClusterGraph data1={cluster} data2={subCluster} />
</Fragment>
</div>
);
}
export default ClusterPage;
Child component:
import '../styles/ClusterGraph.css';
import ForceGraph2D from 'react-force-graph-2d';
import React from "react";
class MyClusterGraph extends React.Component {
constructor(props) {
super(props);
this.state = {nodes:[],links:[]};
}
componentWillMount() {
this.loadData();
}
loadData = async () => {
let nodes = this.props.data1.map(row => {
let id = row.address_id;
let addressHash = row.address_hash;
let nodeColor;
if(row.miner_address)
nodeColor="blue";
else
nodeColor="purple";
return {id:id,addressHash:addressHash,nodeColor:nodeColor};
});
let links = this.props.data2.map(row => {
let source = row.address_id_1;
let target = row.address_id_2;
let linkColor;
switch (row.link_type) {
case 0:
linkColor="green";
break;
case 1:
linkColor="red";
break;
case 2:
linkColor="cyan";
break;
}
return {source:source,target:target,linkColor:linkColor};
});
this.setState({nodes:nodes,links:links});
}
render() {
return (
<div className="graph">
<ForceGraph2D
graphData={this.state}
backgroundColor="white"
height={400}
width={700}
nodeLabel="addressHash"
nodeColor="nodeColor"
linkColor="linkColor" />
</div>
);
}
}
function ClusterGraph({data1,data2}) {
return (
<div className="section2">
<MyClusterGraph data1={data1} data2={data2} />
</div>
);
}
export default ClusterGraph;
Upvotes: 1
Views: 125
Reputation: 343
Try making your parent component calls synchronous currently it is sync individually but it calls in async fashion change your code base accordingly
Upvotes: 0
Reputation: 46073
You could make sure you render your graphs after the data is fully fetched, and show a loader in the meantime, like so:
function ClusterPage(props) {
const [cluster, setCluster] = useState([]);
const [subCluster, setSubCluster] = useState([]);
useEffect(() => {
fetchData1();
fetchData2();
}, []);
const fetchData1 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get("http://localhost:5000/", {
params: { address: address },
});
const data = await response.data;
setCluster(data);
};
const fetchData2 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get("http://localhost:5000/sub/", {
params: { address: address },
});
const data = await response.data;
setSubCluster(data);
};
if (cluster.length <= 0 || subCluster.length <= 0) {
return <p>Loading...</p>;
}
return (
<div key={props.match.params.id}>
<Fragment>
<AddressList data={cluster} />
<ClusterGraph data1={cluster} data2={subCluster} />
</Fragment>
</div>
);
}
This way, you would use the constructor
to format your data, as componentWillMount()
is deprecated and considered unsafe:
class MyClusterGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
nodes: this.props.data1.map((row) => {
let id = row.address_id;
let addressHash = row.address_hash;
let nodeColor;
if (row.miner_address) nodeColor = "blue";
else nodeColor = "purple";
return { id: id, addressHash: addressHash, nodeColor: nodeColor };
}),
links: this.props.data2.map((row) => {
let source = row.address_id_1;
let target = row.address_id_2;
let linkColor;
switch (row.link_type) {
case 0:
linkColor = "green";
break;
case 1:
linkColor = "red";
break;
case 2:
linkColor = "cyan";
break;
}
return { source: source, target: target, linkColor: linkColor };
}),
};
}
render() {
return (
<div className="graph">
<ForceGraph2D
graphData={this.state}
backgroundColor="white"
height={400}
width={700}
nodeLabel="addressHash"
nodeColor="nodeColor"
linkColor="linkColor"
/>
</div>
);
}
}
function ClusterGraph({ data1, data2 }) {
return (
<div className="section2">
<MyClusterGraph data1={data1} data2={data2} />
</div>
);
}
export default ClusterGraph;
Upvotes: 1