Search code examples
reactjsd3.jsreact-d3

React-d3 Line Chart not showing and showing no errors


I'm trying to create my first react d3 line chart but I'm running into a snag. There are no errors showing up when I inspect Chrome so I'm not sure why it's not showing up. I feel like I'm close though.

I've tried to look at other examples but I don't find many with csv examples for some reason.

Here is my code:

import React, { useRef, useEffect, useState } from "react";
import * as d3 from "d3";
import csvData from "../sandbox.csv";

import {
  select,
  line,
  curveCardinal,
  axisBottom,
  axisRight,
  scaleLinear,
} from "d3";

function ActionsLineGraph() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = React.useState(true);

  // const [data, setData] = useState([25, 30, 45, 60, 20, 65, 75]);
  const svgRef = useRef();

  // will be called initially and on every data change
  useEffect(() => {
    d3.csv(csvData).then((data) => {
      // console.log("Fetching Data");
      console.log(data);
      setData(data);
      setLoading(false);

      const svg = select(svgRef.current);

      const xScale = scaleLinear()
        .domain([0, data.length - 1])
        .range([0, 300]);

      const yScale = scaleLinear().domain([0, 150]).range([150, 0]);

      const xAxis = axisBottom(xScale)
        .ticks(data.length)
        .tickFormat((index) => index + 1);
      svg.select(".x-axis").style("transform", "translateY(150px)").call(xAxis);

      const yAxis = axisRight(yScale);
      svg.select(".y-axis").style("transform", "translateX(300px)").call(yAxis);

      // set the dimensions and margins of the graph
      const margin = { top: 20, right: 20, bottom: 50, left: 70 },
        width = 300 - margin.left - margin.right,
        height = 150 - margin.top - margin.bottom;

      // add X axis and Y axis
      const x = d3.scaleTime().range([0, width]);
      const y = d3.scaleLinear().range([height, 0]);

      const parseTime = d3.timeParse("%Y-%m-%d");

      const myLine = d3.line()
                  .x(d =>  x(xScale(parseTime(d.date))))
                  .y(d => y(yScale(Number(d.added))));

/* const myLine = d3.line()
                  .x((d) => { return x(parseTime(d.date)); })
                  .y((d) => { return y(Number(d.added)); }); */
     
      svg
        .append("path")
        .data([data])
        .attr("class", "line")
        .attr("fill", "none")
        .attr("stroke", "steelblue")
        .attr("stroke-width", 10)
        .attr("d", myLine)
        .style('overflow', 'visible')
        // .style("transform", "translate(500px, 150px)");
    });
  }, []);

  return (
    <React.Fragment>
      <svg ref={svgRef}>
        <g className="x-axis" />
        <g className="y-axis" />
      </svg>
      
    </React.Fragment>
  );
}

export default ActionsLineGraph;

Here is my csv data I'm using:

date,added,updated,deleted
2021-09-15,10,9,8
2021-09-16,20,11,7
2021-09-17,15,12,9
2021-09-18,20,9,8
2021-09-19,20,9,8

Currently, it just shows the tips of the axes enter image description here

Any and all help or direction is appreciated.


Solution

  • Your code contains some errors. See a solution in the snippet:

    const csvData = `date,added,updated,deleted
    2021-09-15,10,9,8
    2021-09-16,20,11,7
    2021-09-17,15,12,9
    2021-09-18,20,9,8
    2021-09-19,20,9,8
    `;
    
    const ActionsLineGraph = (props) => {
      const svgRef = React.useRef();
    
      // will be called initially and on every data change
      React.useEffect(() => {
        const data = d3.csvParse(csvData);
        console.log(data);
        const parseTime = d3.timeParse("%Y-%m-%d");
    
        const svg = d3.select(svgRef.current);
        const from = parseTime(data[0].date);
        const to = parseTime(data[data.length-1].date);
        console.log('FROM: ', from);
        console.log('TO: ', to);
    
        const xScale = d3.scaleTime()
            .domain([to, from])
            .range([300, 0]);
    
          const yScale = d3.scaleLinear().domain([0, 30]).range([150, 0]);
    
          const xAxis = d3.axisBottom(xScale)
            .ticks(data.length)
            .tickFormat((index) => index + 1);
          svg.select(".x-axis").style("transform", "translateY(150px)").call(xAxis);
    
          const yAxis = d3.axisRight(yScale);
          svg.select(".y-axis").style("transform", "translateX(300px)").call(yAxis);
    
          // set the dimensions and margins of the graph
          const margin = { top: 20, right: 20, bottom: 50, left: 70 },
            width = 300 - margin.left - margin.right,
            height = 150 - margin.top - margin.bottom;
    
          // add X axis and Y axis
          const x = d3.scaleTime().range([0, width]);
          const y = d3.scaleLinear().range([height, 0]);
    
        
          const path = data.reduce((path, item, index) => {
            const x = xScale(parseTime(item.date));
            const y = yScale(Number(item.added));
            const point = `${x},${y}`;
            return index === 0 ? `M ${point}` : `${path} L ${point}`;
          }, null);
          
          console.log('PATH: ', path);
    
          svg.append("path")
            .attr("class", "line")
            .attr("fill", "none")
            .attr("stroke", "steelblue")
            .attr("stroke-width", 10)
            .attr("d", path)
      }, [svgRef]);
    
      return (
          <svg ref={svgRef}>
            <g className="x-axis" />
            <g className="y-axis" />
          </svg>
      );
    }
    
    ReactDOM.render(<ActionsLineGraph />, document.querySelector("#app"))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js"></script>
    
    
    <div id="app"></div>