<div id="root"></div>
body {
  margin: 0;
}

.d3-component svg {
  background: #ddd;
  height: 300px;
  max-height: 300px;
}
const size = 600
const defaultRadius = 20

const level2Colors = level => {
  const colors = {
    1: "#A01928",
    2: "#9F62A4",
    3: "#0B7A7A",
    4: "#3F3F38",
  }

  return colors[level]
}

const level2Rad = level => {
  return (2.5 * defaultRadius) / level
}

const loadData = () => {
  const nodes = [
    { id: 1, level: 1, r: 8 },
    { id: 2, level: 2, r: 8 },
    { id: 3, level: 2, r: 8 },
    { id: 4, level: 2, r: 8 },
    { id: 5, level: 3, r: 8 },
    { id: 6, level: 3, r: 8 },
    { id: 7, level: 3, r: 8 },
    { id: 8, level: 3, r: 8 },
    { id: 9, level: 3, r: 8 },
    { id: 10, level: 3, r: 8 },
    { id: 11, level: 4, r: 8 },
    { id: 12, level: 4, r: 8 },
    { id: 13, level: 4, r: 8 },
    { id: 14, level: 4, r: 8 },
    { id: 15, level: 4, r: 8 },
    { id: 16, level: 4, r: 8 },
  ]

  const links = [
    { source: 1, target: 2 },
    { source: 1, target: 3 },
    { source: 1, target: 4 },
    { source: 2, target: 5 },
    { source: 2, target: 6 },
    { source: 3, target: 7 },
    { source: 3, target: 8 },
    { source: 3, target: 9 },
    { source: 4, target: 5 },
    { source: 4, target: 9 },
    { source: 4, target: 10 },
    { source: 5, target: 11 },
    { source: 5, target: 12 },
    { source: 7, target: 13 },
    { source: 8, target: 14 },
    { source: 10, target: 15 },
    { source: 10, target: 16 },
    { source: 9, target: 16 },
  ]

  return { nodes, links }
}

class CustomD3Component extends React.Component {
  componentDidMount() {
    const node = document.querySelector("#d3Node")
    console.log(node)
    this.initialize(node, {})
  }
  
  initialize = (node, props) => {
    function dragstarted(d) {
      if (!d3.event.active) simulation.alphaTarget(0.3).restart()
      d.fx = d.x
      d.fy = d.y
    }

    function dragged(d) {
      d.fx = d3.event.x
      d.fy = d3.event.y
    }

    function dragended(d) {
      if (!d3.event.active) simulation.alphaTarget(0)
      d.fx = null
      d.fy = null
    }
    const svg = (this.svg = d3.select(node).append("svg"))
    svg
      .attr("viewBox", `0 0 ${size} ${size}`)
      .style("width", "100%")
      .style("height", "auto")

    const simulation = d3
      .forceSimulation()
      .force(
        "link",
        d3
          .forceLink()
          .id(function(d) {
            return d.id;
          })
          .distance(100)
      )
      .force(
        "collide",
        d3
          .forceCollide(function(d) {
            return level2Rad(d.level) + 3
          })
          .iterations(16)
      )
      .force("charge", d3.forceManyBody().strength(-800))
      .force("center", d3.forceCenter(size / 2, size / 2))
      .force("y", d3.forceY(-100))
      .force("x", d3.forceX(-100))

    const data = loadData()

    var link = svg
      .append("g")
      .attr("class", "links")
      .selectAll("line")
      .data(data.links)
      .enter()
      .append("line")
      .attr("stroke", "grey")

    var node = svg
      .append("g")
      .attr("class", "nodes")
      .selectAll("circle")
      .data(data.nodes)
      .enter()
      .append("circle")
      .attr("fill", d => level2Colors(d.level))
      .attr("r", function(d) {
        return level2Rad(d.level)
      })
      .call(
        d3
          .drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended)
      )

    var ticked = function() {
      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
        })

      node
        .attr("cx", function(d) {
          return d.x
        })
        .attr("cy", function(d) {
          return d.y
        })
    }

    simulation.nodes(data.nodes).on("tick", ticked)
    simulation.force("link").links(data.links);
  }
  
  render() {
    return <div id="d3Node" className="d3-component"></div>;
  }
}

ReactDOM.render(<CustomD3Component />, document.getElementById("root"))
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js