Put a disjoint force directed graph into React app

Viewed 257

So I have a json file that contains a nodes and link array. The nodes contains a movie name which then maps to the actors who played in it. The links combines the movie to the actor. This file is stored in another directory and the file path is "../Data/nodes.json" Here is a snippet from nodes (Just one movie because the entire file is 32k lines long)

"nodes": [
  {
   "name": "Guardians of the Galaxy",
   "id": "1"
  },
  {
   "name": "Chris Pratt"
  },
  {
   "name": "Vin Diesel"
  },
  {
   "name": "Bradley Cooper"
  },
  {
   "name": "Zoe Saldana"
  }
]

And here's a snippet from links

"links": [
  {
   "source": "Guardians of the Galaxy",
   "target": "Chris Pratt"
  },
  {
   "source": "Guardians of the Galaxy",
   "target": "Vin Diesel"
  },
  {
   "source": "Guardians of the Galaxy",
   "target": "Bradley Cooper"
  },
  {
   "source": "Guardians of the Galaxy",
   "target": "Zoe Saldana"
  }
]

My goal is to try and have this disjoint force directed graph showing up on a React GUI after making it with D3. I haven't been able to find any tutorials that could help. Here is my App.js currently...

import React from 'react';
import './App.css';
import * as d3 from 'd3';
import { Fetch } from 'react-request';

class App extends React.Component{
    constructor(props) {
        super(props);
        this.myRef = React.createRef();
    }

    componentDidMount() {
        const nodes = new Request('../Data/nodes.json');
        fetch(nodes).then(response => response.json()).then(data => {
            //loops below just display contents of our nodes.json file in the browser's console
            //it takes FOREVER to log these
           /** for (const obj of data['nodes']){
            console.log("Name: " + obj['name']);
        }

             for (const obj of data['links']){
            console.log("source: " + obj['source']);
            console.log("target: " + obj['target']);
        }*/

                //create somewhere to put the force directed graph
            let svg = d3.select("svg"),
                width = +svg.attr("width"),
                height = +svg.attr("height");


            const nodes_data = data['nodes'];


            //set up the simulation
            //nodes only for now
            let simulation = d3.forceSimulation()
                //add nodes
                .nodes(nodes_data);

            //add forces
            //we're going to add a charge to each node
            //also going to add a centering force
            simulation
                .force("charge_force", d3.forceManyBody())
                .force("center_force", d3.forceCenter(width / 2, height / 2));


            //draw circles for the nodes
            let node = svg.append("g")
                .attr("class", "nodes")
                .selectAll("circle")
                .data(nodes_data)
                .enter()
                .append("circle")
                .attr("r", 5)
                .attr("fill", "red");


            //add tick instructions:
            simulation.on("tick", tickActions);


            //Time for the links

            //Create links data
            const links_data = data['links'];


            //Create the link force
            //We need the id accessor to use named sources and targets

            let link_force = d3.forceLink(links_data)
                .id(function (d) {
                    return d.name;
                })

            //Add a links force to the simulation
            //Specify links  in d3.forceLink argument


            simulation.force("links", link_force)

            //draw lines for the links
            let link = svg.append("g")
                .attr("class", "links")
                .selectAll("line")
                .data(links_data)
                .enter().append("line")
                .attr("stroke-width", 2);


            function tickActions() {
                //update circle positions each tick of the simulation
                node
                    .attr("cx", function (d) {
                        return d.x;
                    })
                    .attr("cy", function (d) {
                        return d.y;
                    });

                //update link positions
                //simply tells one end of the line to follow one node around
                //and the other end of the line to follow the other node around
                link
                    .attr("x1", function (d) {
                        return d.source.x;
                    })
                    .attr("y1", function (d) {
                        return d.source.y;
                    })
                    .attr("x2", function (d) {
                        return d.target.x;
                    })
                    .attr("y2", function (d) {
                        return d.target.y;
                    });

            }
        });

    }

    render(){
        return (
            <div ref={this.myRef}>
            </div>
        );
    }
}
export default App;

I'd also like to be able to add a NavBar.js class I made to the top of the GUI in the future as well if possible.

1 Answers

You select 'svg' on mount, but it does not exist at this point. Add it to the render:

<div ref={this.myRef}>
  <svg />
</div>

... and then select svg correctly on mount:

const svg = d3.select(this.myRef).select('svg')
Related