<div id="root"></div>
path {
  stroke-linejoin: round;
  stroke-linecap: round;
}

.districts {
  fill: #bbb;
}

.districts :hover {
  fill: orange;
}

.district-boundaries {
  pointer-events: none;
  fill: none;
  stroke: #fff;
  stroke-width: .5px;
}

.state-boundaries {
  pointer-events: none;
  fill: none;
  stroke: #fff;
  stroke-width: 1.5px;
}

class CongressionalDistricts extends React.Component {
    constructor() {
      super()
      this.state = {
        usData: null,
        usCongress: null
      }
    }

    componentWillMount() {
        d3.queue()
          .defer(d3.json, "https://raw.githubusercontent.com/Swizec/113th-congressional-districts/master/public/us.json")
          .defer(d3.json, "https://raw.githubusercontent.com/Swizec/113th-congressional-districts/master/public/us-congress-113.json")
          .await((error, usData, usCongress) => {
              this.setState({
                  usData,
                  usCongress
              });
          })
    }

    componentDidUpdate() {
        const svg = d3.select(this.refs.anchor),
              { width, height } = this.props;

        const projection = d3.geoAlbers()
                             .scale(1280)
                             .translate([width / 2, height / 2]);

        const path = d3.geoPath(projection);

        const us = this.state.usData,
              congress = this.state.usCongress;

        svg.append("defs").append("path")
           .attr("id", "land")
           .datum(topojson.feature(us, us.objects.land))
           .attr("d", path);

        svg.append("clipPath")
           .attr("id", "clip-land")
           .append("use")
           .attr("xlink:href", "#land");

        svg.append("g")
           .attr("class", "districts")
           .attr("clip-path", "url(#clip-land)")
           .selectAll("path")
           .data(topojson.feature(congress, congress.objects.districts).features)
           .enter().append("path")
           .attr("d", path)
           .append("title")
           .text(function(d) { return d.id; });

        svg.append("path")
           .attr("class", "district-boundaries")
           .datum(topojson.mesh(congress, congress.objects.districts, function(a, b) { return a !== b && (a.id / 1000 | 0) === (b.id / 1000 | 0); }))
           .attr("d", path);

        svg.append("path")
           .attr("class", "state-boundaries")
           .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
           .attr("d", path);
    }

    render() {
        const { usData, usCongress } = this.state;

        if (!usData || !usCongress) {
            return null;
        }

        return <g ref="anchor" />;
    }
}


class App extends React.Component {
  render() {
    return (
        <div className="App">
            <svg width="960" height="600">
                <CongressionalDistricts width={960} height={600} />
            </svg>
      </div>
    );
  }
}


ReactDOM.render(<App />, 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/4.9.1/d3.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.0/topojson.js