title: How to add only new nodes to d3 force directed graph?
body: <p>I have created a force directed graph using <code>d3</code>, which is rendering in a <code>react</code> component using the <code>useEffect</code> hook. The graph is initially rendered correctly, but if it is re-rendered/updated by new nodes being submitted through an input form, the existing nodes are duplicated. </p>
  
  <p>I thought that the existing nodes would be left alone and only new nodes would be created after <code>.enter()</code>, which is clearly not happening. Any help with where I'm going wrong?</p>
  
  <p><strong>Edit 1:</strong> this is a sample of the data coming in</p>
  
  <pre><code>var nodesData = [
  {"id": "Do Something", "type": "activity"},
  {"id": "My Document", "type": "object"}
  ]
  </code></pre>
  
  <p>This is the code for the graph:</p>
  
  <pre><code>import React, { useRef, useEffect } from 'react';
  import * as d3 from 'd3';
  import '../../custom_styles/bpForceDirected.css';
  
  interface IProps {
      data?: string;
      linkData?: string;
  }
  
  /* Component */
  export const BpForceDirectedGraph = (props: IProps) =&gt; {
  
      const d3Container = useRef(null);
  
      /* The useEffect Hook is for running side effects outside of React,
         for instance inserting elements into the DOM using D3 */
      useEffect(
          () =&gt; {
              if (props.data &amp;&amp; d3Container.current) {
                  var w=500;
                  var h=500;
                  const svg = d3.select(d3Container.current)
                              .attr("viewBox", "0 0 " + w + " " + h )
                              .attr("preserveAspectRatio", "xMidYMid meet");
  
                  var simulation = d3.forceSimulation()
                      .nodes(props.data);
                  simulation
                      .force("charge_force", d3.forceManyBody())
                      .force("center_force", d3.forceCenter(w / 2, h / 2));
  
                  function circleColor(d){
                      if(d.type ==="activity"){
                          return "blue";
                      } else {
                          return "pink";
                      }
                  }
  
                  function linkColor(d){
                      console.log(d); 
                      if(d.type === "Activity Output"){
                          return "green";
                      } else {
                          return "red";
                      }
                  }
  
                  //Create a node that will contain an object and text label      
                  var node = svg.append("g")
                    .attr("class", "nodes")
                    .selectAll("g")
                    .data(props.data)
                    .enter()
                    .append("g");
  
                  node.append("circle")
                          .attr("r", 10)
                          .attr("fill", circleColor);
  
                  node.append("text")
                      .attr("class", "nodelabel")
                      .attr("dx", 12)
                      .attr("dy", ".35em")
                      .text(function(d) { return d.activityname });
  
                  // The complete tickActions() function    
                  function tickActions() {
                  //update circle positions each tick of the simulation 
                      node.attr('transform', d =&gt; `translate(${d.x},${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; });
                  }  
  
                    simulation.on("tick", tickActions );
  
                  //Create the link force 
                  //We need the id accessor to use named sources and targets 
                  var link_force =  d3.forceLink(props.linkData)
                      .id(function(d) { return d.id; })
  
                  simulation.force("links",link_force)
  
                  //draw lines for the links 
                  var link = svg.append("g")
                      .attr("class", "links")
                      .selectAll("line")
                      .data(props.linkData)
                      .enter().append("line")
                          .attr("stroke-width", 2)
                          .style("stroke", linkColor);
  
                  // Remove old D3 elements
                  node.exit()
                      .remove();
              }
          },
  
          [props.data, props.linkData, /*d3Container.current*/])
  
      return (
          &lt;svg
              className="d3-component"
              ref={d3Container}
          /&gt;
      );
  }
  
  export default BpForceDirectedGraph;
  </code></pre>
  
  <p><strong>Edit 2:</strong> <a href="https://codesandbox.io/s/distracted-nightingale-f4nnw" rel="nofollow noreferrer">Reproducible code example</a></p>