I have been doing a project where I present a graph on the screen. Now, the data
state within the App.js is being passed as a prop and then causes an infinite loop error
.
If the data variable is within the Graph.js file and used and defined within the useState
, then there is no issue.
I was a bit uncertain why this was happening as I am relatively new to react and javascript.
The goal is to add the data to the graph, it loads, then when I change the data within the App.js
file, the page will automatically load again with the new data.
Any help/advice will be much appreciated :)
App.js
function App() {
const [data, setData] = useState(null)
setData(
{
nodes:[
{name:"Max"},
{name:"George"},
{name:"Jesus"},
{name:"Ben"},
{name:"James"},
{name:"Sam"},
{name:"Sassms"}
],
edges:[
{source:"Max", target:"George"},
{source:"George", target:"Jesus"},
{source:"Jesus", target:"Max"},
{source:"Jesus", target:"Ben"},
{source:"James", target:"Ben"},
{source:"Sam", target:"Sassms"},
{source:"Sam", target:"Ben"}
]
})
console.log(data)
return (
<Graph colour="Grey" data={data} setData={setData}/>
);
}
Graph.js
function Graph(props) {
const svgRef = useRef(null);
useEffect(
() => {
let svg = d3.select(svgRef.current)
if(svgRef.current) {// Check that svg element has been rendered
let width = svg.attr("width")
let height = svg.attr("height")
d3.selectAll("svg > *").remove();
let edge = svg
.append("g")
.selectAll("line")
.data(props.data.edges)
.enter()
.append("line")
.attr("stroke-width", function(d) {
return 3;
})
.style("stroke", "black")
let node = svg
.append("g")
.selectAll("circle")
.data(props.data.nodes)
.enter()
.append("circle")
.attr("r", 7)
.attr("fill", function(d) {
return "grey";
})
.attr("stroke", "black")
}
}, [props.data])
return (
<svg ref={svgRef} id="svgID" width="1000" height="900"></svg>
)
}
CodePudding user response:
It's because you're updating the state on every render again and again. If you just simply call setData()
inside the function, it will call this setter that invokes a re-render and triggers React to call your App
function again.
If you want to have a default state, you can do it like below. If you want to update the state, it should be done by other actions, like the click of a button, fetching some data, etc.
Check out the demo below where I added a button that adds a new name to your nodes
array:
function App() {
const [data, setData] = React.useState({
nodes: [
{ name: 'Max' },
{ name: 'George' },
{ name: 'Jesus' },
{ name: 'Ben' },
{ name: 'James' },
{ name: 'Sam' },
{ name: 'Sassms' },
],
edges: [
{ source: 'Max', target: 'George' },
{ source: 'George', target: 'Jesus' },
{ source: 'Jesus', target: 'Max' },
{ source: 'Jesus', target: 'Ben' },
{ source: 'James', target: 'Ben' },
{ source: 'Sam', target: 'Sassms' },
{ source: 'Sam', target: 'Ben' },
],
});
return <Graph colour="Grey" data={data} setData={setData} />;
}
function Graph(props) {
function addRandomName() {
props.setData({
...props.data,
nodes: [
...props.data.nodes,
{ name: Math.random().toString(36).slice(2) },
],
});
}
return (
<React.Fragment>
<button onClick={addRandomName}>Add new name</button>
<h2>Names</h2>
<p> {props.data.nodes.map((d) => d.name).join(', ')}
</p>
<h2>State</h2>
<pre>{JSON.stringify(props.data, null, 2)}</pre>
</React.Fragment>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>