JSFiddle - React, Tailwind, and code Playground

HTML

<input type="submit" id="clickme" value="click me to update" />
<div id="viz"></div>
<!-- The goal: to end up with a <g> element for each airport,
containing a circle for each flight, and a text element for the 
airport name - and to get transitions working correctly -->

CSS

.LHR { 
    color: blue;
    fill: blue;
}
.SFO { 
    color: red;
    fill: red;
}
.LAX { 
    color: green;
    fill: green;
}
#viz { 
    margin-top: 10px;
    margin-left: 10px;
}

JavaScript

var data1 = [
    { 'name': 'LHR', 'position': 1, 'flights': [1,10,12,14,3] },  
    { 'name': 'LAX', 'position': 2, 'flights': [3,6,12,6,3] },
    { 'name': 'SFO', 'position': 3, 'flights': [2,8,17,18,5] }
];
var data2 = [
    { 'name': 'CDG', 'position': 1, 'flights': [4,5,11,9,1] },
    { 'name': 'LHR', 'position': 2, 'flights': [2,9,11,12,5] },  
    { 'name': 'SFO', 'position': 3, 'flights': [4,5,6,9,5] }
];

var h = 500;
var vis = d3.select("#viz").append("svg").attr("width", 300).attr("height", 500);

function update(data) { 
    
  console.log('update', data); 

  var g = vis.selectAll("g.airport").data(data, function(d) { 
   return d.name;  
  });
    
  // Add new group elements to the enter selection. 
  var gEnter = g.enter().append("g")
    .attr("class", function(d) {    
      return "airport " + d.name;
    });

 // Update the position of existing legends.   
  g.selectAll("text.legend")
  .transition().duration(1000)
   .attr("y", function(d,i, j) { return j * 20 + 20; });
    
  // Append new legends to elements in the enter selection. 
  gEnter.append("text").attr("class", function(d) {
    return d.name + ' legend';
  })
  .attr("x", 250)
  .attr("y", 0)
  .attr("dy", ".2em").text(function(d, i) {
    return d.name;
  })
  .transition().duration(1000)
   .attr("y", function(d,i) { return i * 20 + 20; });

  // Bind data to existing circles. 
  var circle = g.selectAll("circle.flight")
    .data(function(d, i) { return d.flights; }); 
    
  // Transition circles in the update selection.
   circle.transition().duration(1000)
  .attr("cy", function(d,i) { return d*2 + 10 })
  .attr("r", "3");
   
  // Position circles in the enter selection. 
  circle.enter().append("circle").attr("class", 'flight')
    .attr("cx", function(d,i) { return i * 20 + 20; })
    .attr("cy", 0)
    .attr("r", "3")
    .transition().duration(1000)
    .attr("cy", function(d,i) { return d*2 + 10 });
    
  // Remove exited elements. 
  g.exit().transition()
     ...