Search code examples
reactjsd3.jslabelordinal

How to make x axis labels work in d3 with react?


here is an example of my code:

import React, {Component} from 'react';
import {scaleOrdinal, axisBottom, select} from 'd3';

export default class BarChart extends Component {

  constructor(props) {
    super(props)
    this.renderSvg = this.renderSvg.bind(this);
  }

  componentDidMount() {
    this.renderSvg();
  }

  renderSvg() {
    const margin = 20;
    const width = 400;
    const height = 800;
    const data = ["why", "aren't", "you", "working"]
      .map((title) => ({title}));

    // create scalar for x axis
    const x = scaleOrdinal()
      .domain(data.map(({title}) => title))
      .range([0, width]);

    const xAxis = axisBottom(x);

    // create svg and append graph
    const svg = select("svg")
      .attr("width", width)
      .attr("height", height + margin)
      .append("g")

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);
  }

  render() {
    const {width, height, className} = this.props;
    return <div className="body">
      <svg className="svg" />
    </div>
  }
}

I have followed the example at this site: https://bl.ocks.org/mbostock/3259783 pretty closely as far as I can tell, but I can't get it to display the labels where they are expected to be at regular/even intervals. Any help would be appreciated, I'm new to using d3.


Solution

  • The problem is the conversion from d3v3 to d3v4/5.

    The d3.scale.ordinal() (v3) is used for a number of scale types and determined by the rangeX() call you make.

    In d3v5 d3.scaleOrdinal() means an ordinal scale and maps an ordinal domain to an ordinal range. https://github.com/d3/d3-scale/#scaleOrdinal You have only set the range to a 2 value array. So each domain value is alternated on the 2 range values.

    Here is a d3v5 example. I have adjusted the height to see something when run. Use the Developer Tools to see the alternating translation of the axis ticks.

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    </head>
    <body>
    <div class="body">
    <svg class="svg"/>
    </div>
    <script>
    function renderSvg() {
      const margin = 20;
      const width = 400;
      const height = 100;
      const data = ["why", "aren't", "you", "working"]
        .map((title) => ({title}));
    
      // create scalar for x axis
      const x = d3.scaleOrdinal()
        .domain(data.map(({title}) => title))
        .range([0, width]);
    
      const xAxis = d3.axisBottom(x);
    
      // create svg and append graph
      const svg = d3.select("svg")
        .attr("width", width)
        .attr("height", height + margin)
        .append("g");
    
      svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
    }
    
    renderSvg();
    
    </script>
    </body>
    </html>

    The example code uses .rangePoints([0, width]) and that converts in d3v5 to d3.scalePoint(). Now the ordinal domain is mapped to a continuous range.

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    </head>
    <body>
    <div class="body">
    <svg class="svg"/>
    </div>
    <script>
    function renderSvg() {
      const margin = 20;
      const width = 400;
      const height = 100;
      const data = ["why", "aren't", "you", "working"]
        .map((title) => ({title}));
    
      // create scalar for x axis
      const x = d3.scalePoint()
        .domain(data.map(({title}) => title))
        .range([0, width]);
    
      const xAxis = d3.axisBottom(x);
    
      // create svg and append graph
      const svg = d3.select("svg")
        .attr("width", width)
        .attr("height", height + margin)
        .append("g");
    
      svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
    }
    
    renderSvg();
    
    </script>
    </body>
    </html>