DEV Community

Gil Fink
Gil Fink

Posted on • Originally published at Medium on

Creating a Force Graph using React and D3

Lately, I was involved in an interesting project that includes building various graph visualizations. The app we built used React as the view engine and D3 library for data visualization. In this post I’ll show you how to create a force graph using D3 for visualizing connections in your data.

Creating The Example App

We’ll start by creating a new React app using create-react-app bootstraper. If you have Node.js installed on your machine, just run the following command:



npx create-react-app react-d3-force


Enter fullscreen mode Exit fullscreen mode

This command will generate a new React project. After the project was created, get into the app folder and add D3 and Font Awesome libraries to it by using the following command:



npm i d3 @fortawesome/fontawesome-free


Enter fullscreen mode Exit fullscreen mode

Now that all the libraries we need are in the project, it’s time to move forward and create the project structure:

Project Structure

A few things to notice here:

  • Under components folder we have two components: forceGraph and forceGraphGenerator. The ForceGraph component will be the container for the generated force graph and ForceGraphGenerator will generate the graph using D3.
  • Under the data folder we will find a JSON file with the graph data. Of course this data should be retrieve from the server but for simplicity of the example I use it inside the project.

Building the Force Graph Container

The force graph container will contain the graph and will be responsible to things like data manipulation or business logic functionality. For simplicity in project we are creating it with a reference to the div which will wrap the generate graph and nothing more.

Here is the code for the ForceGraph component container:

The only interesting thing that happens here is the usage of a React ref to reference the container element. Another thing is the destroy function that we will get from the runForceGraph , which will handle the clean up if the component is removed from the DOM.

Building the Force Graph Generator

The force graph generator will be a function that will be responsible to generate the graph. Here is the declaration of the function which gets the containing div, the data for links and nodes and a function to generate a node tooltip:

Now let’s implement it.

The first lines of code will be to copy the data and to get the container’s width and height :

Then, we’ll add a few helper functions:

The 3 functions will retrieve the color, icon and CSS class for a given node. The last function will add the option to drag the force graph nodes as part of it’s simulation.

After the previous part we will generate the code that will handle the node tooltip generation:

The addTooltip and removeTooltip are helper functions to show/hide the tooltip with it’s relevant content.

Now that we have everything in place, we will add the D3 code to generate the graph:

The code creates force simulation and add to it the nodes and the links. It is also responsible to add for each node an icon (this is why we added Font Awesome ) and color.

When the graph is ready we will add a few event handlers to handle what is going to happen when tick is happening or when tooltip is needed to be shown:

Last but not least we will return the destroy function that the graph container is going to use:

The whole function source code:

And here you can find the force graph CSS module:

The Data Used in the Example

The data.json file the we load:

The App Implementation

The App component will run the entire graph. Here is its source code:

Now that everything is set in place you can run the app and look at your fancy force graph.

The Force Graph

Summary

In the post I showed how to create a force graph component using React and D3 libraries. You can find the graph code here.

Top comments (8)

Collapse
 
joacorma profile image
joacorma

Great post! This is really useful since i was struggling with React + D3 workflow.
I'm now trying to manage updating the visualization using a wrapper's state and then passing it to the props of the forceGraph.
Do you think I should force D3 to rerun the simulation, or which is the correct way to perform this update? Thank you!

Collapse
 
gilfink profile image
Gil Fink

Thanks for reading.

Sorry for the late response. In the force graph container you can add to the React.useEffect that runs the force graph creation any dependency in it's dependency array. If something changes in one of the dependencies the effect will run again and will re-render the graph.

Collapse
 
danieltkach profile image
Daniel Tkach

Hey Gil, thanks for the awesome article. What this is doing for me is creating a new graph inside the same container, just below the other one... any ideas? This is really puzzling. I appreciate your help very much.

Thread Thread
 
gilfink profile image
Gil Fink

Hi @danieltkach
Thanks for reading. It isn't puzzling but rather the expected behavior. Since the runForceGraph inner code selects the container and adds to it all the relevant underline SVG parts for the force graph. You can clean/clear the content of the container when the effects run to make sure things aren't recreated again.
Let me know if the answer helped you.

Thread Thread
 
danieltkach profile image
Daniel Tkach

Hey Gil! Ok, the question is HOW, how do I do that. Could you show me how to do it?

Thread Thread
 
gilfink profile image
Gil Fink

Look for the following line in the runForceGraph:
const containerRect = container.getBoundingClientRect();
Add before that line something like:
container.innerHTML = '';
Another option is to clean/clear the container in the useEffect of the containing component.

Thread Thread
 
danieltkach profile image
Daniel Tkach

container.innerHTML = '';
did not work.

How do you clear that container, like the second option you described?
Having the graph react to data change is very important for me.
Thanks.

Collapse
 
danieltkach profile image
Daniel Tkach

Hi Gil, any updates on this?