reactjs – React loading data for d3 linechart

I’m both new to d3 and React, and I’m trying to draw a linechart with d3 with data I get from my backend through an async rest call. When I used static data from a .csv file, it worked. But when I try to use the backend data, neither the xScale is on the correct position nor the lines are shown.

As far I can see from my console outputs, the data is formatted and fetched correctly when I’m trying to draw the lines. Since neither the xScale or my lines are visible, my guess is that the zoom also doesn’t work anymore.

Hope someone can help me.


type ChartDataType = {
  timestamp: Date,
  value: number
}

const LineChart = ({
                     width, height, top, right, bottom, left, fill, url, equipmentUUID
                   }: ZoomBasicLineChartProps) => {
  const dispatch = useDispatch<AppDispatch>()
  const [data, setData] = useState<ChartDataType[]>()
  const title = useRef<string>('Default title, use label')
  const containerRef = useRef<HTMLDivElement>()
  let container: Selection<SVGGElement, unknown, HTMLElement, unknown>;
  let zoom: d3.ZoomBehavior<HTMLDivElement, unknown>;

  const chartWidth = width - left - right
  const chartHeight = height - top - bottom

  let xAxis: Selection<SVGGElement, unknown, HTMLElement, unknown>;
  let yAxis: Selection<SVGGElement, unknown, HTMLElement, unknown>;
  let path: any;

  const diagramDTO = useSelector(LineChartDiagramSelectors.strippedLineChartDiagramData)

  const loadStrippedLineChartDiagramData = useCallback(() => {
    dispatch(LineChartDiagramDataThunks.getLineChartDiagramDataArray("services/performancemanagement/api/diagram/line-chart/7f5a2e69-0a51-4131-a77f-601ae9de24c6/0/SchlagstatistikGes_VR1?since=148"))
  }, [dispatch])


  const containerSelection = useMemo(() => (
    d3.select<HTMLDivElement, unknown>('#ZoomLineChart')
  ), [containerRef.current])


  const xScale = useMemo(() => {
    const domain = data ? d3.extent(data, (d) => d.timestamp) : [new Date(), new Date()]
    console.log("domain")
    console.log(domain)
    return d3
      .scaleTime()
      .domain([domain[0] ?? new Date(), domain[1] ?? new Date()])
      .range([0, width])
  }, [data])

  const yScale = useMemo(() => {
    const domain = data ? d3.extent(data, (d) => d.value) : [0, 0]
    return d3
      .scaleLinear()
      .domain([domain[0] ?? 0, domain[1] ?? 0])
      .range([height, 0])
  }, [data])

  const initSvg = (): SelectionType => (
    containerSelection
      .append('svg')
      .attr('width', chartWidth + left + right)
      .attr('height', chartHeight + top + bottom + 75)
      .append('g')
      .attr('transform', `translate(${left},${top})`)
  )


  const drawAxes = (g: SelectionType): void => {
    xAxis = g.append('g')
      .call(d3.axisBottom(xScale))
    yAxis = g.append('g')
      .call(d3.axisLeft(yScale))
  }

  const drawLabel = (g: SelectionType): void => {
    g.append('text')
      .attr('text-anchor', 'start')
      .attr('y', height + 40)
      .attr('x', 0)
      .text(title.current)
  }


  const drawLines = (g: SelectionType): void => {
    if (data) {
      const lines = d3.line<ChartDataType>()
        .x(d => {
          // console.log(d.timestamp)
          return xScale(d.timestamp)
        })
        .y(d => {
          // console.log(d.value)
          return yScale(d.value)
        })
      console.log("data")
      console.log(data)

      path = g
        .append('g')
        .attr('clip-path', "url(#clip)")
        .append('path')
        .datum(data)
        .attr('class', 'line')
        .attr('fill', 'none')
        .attr('stroke', fill)
        .attr('stroke-width', 1.5)
        .attr('d', lines)
    }
  }

  function updateChart(event: any) {
    const {transform} = event;
    const newX = transform.rescaleX(xScale);
    xAxis.call(d3.axisBottom(newX));

    path.attr("d", d3.line<ChartDataType>()
      .x(d => newX(d.timestamp))
      .y(d => yScale(d.value)));
  }

  const clip = (g: SelectionType): void => {
    g.append("defs")
      .append("SVG:clipPath")
      .attr("id", "clip")
      .append("SVG:rect")
      .attr("width", width)
      .attr("height", height)
      .attr("x", 0)
      .attr("y", 0);
  }

  const initZoom = (g: SelectionType): void => {
    zoom = d3.zoom<HTMLDivElement, unknown>()
      .scaleExtent([0.5, 5])
      .on('zoom', updateChart)
    containerSelection.call(zoom)
  }

  // Parse into data object
  useEffect(() => {
    if (diagramDTO) {
      const updatedData = diagramDTO.payload?.map(dataPoint => {
        const value = +dataPoint.value
        return {timestamp: dataPoint.timestamp, value}
      })
      setData(updatedData)
    } else {
      loadStrippedLineChartDiagramData()
    }
  }, [diagramDTO])


  useEffect(() => {
    container = initSvg();
    initZoom(container)
    clip(container)
    drawAxes(container)
    drawLines(container)
    drawLabel(container)
  }, [data])

  return (
    <>
      <Box id='ZoomLineChart' ref={containerRef}/>
    </>
  )
}

Example data playload:

{
  "unit" : "unit.schlagstatistikges_vr1",
  "label" : "label.schlagstatistikges_vr1",
  "payload" : [ {
    "timestamp" : "2022-06-08T03:22:00Z",
    "value" : "10676"
  }, {
    "timestamp" : "2022-06-08T03:23:00Z",
    "value" : "10583"
  }, {
    "timestamp" : "2022-06-08T03:24:00Z",
    "value" : "10647"
  }, {
    "timestamp" : "2022-06-08T03:25:00Z",
    "value" : "10585"
  }, {
    "timestamp" : "2022-06-08T03:26:00Z",
    "value" : "10644"
  }, {
    "timestamp" : "2022-06-08T03:27:00Z",
    "value" : "10227"
  }, {
    "timestamp" : "2022-06-08T03:28:00Z",
    "value" : "10620"
  }, {
    "timestamp" : "2022-06-08T03:29:00Z",
    "value" : "10635"
  }, {
    "timestamp" : "2022-06-08T03:30:00Z",
    "value" : "10432"
  }, {
    "timestamp" : "2022-06-08T03:31:00Z",
    "value" : "10295"
  }, {
    "timestamp" : "2022-06-08T03:32:00Z",
    "value" : "10674"
  }, {
    "timestamp" : "2022-06-08T03:33:00Z",
    "value" : "10715"
  }, {
    "timestamp" : "2022-06-08T03:34:00Z",
    "value" : "10068"
  }, {
    "timestamp" : "2022-06-08T03:35:00Z",
    "value" : "10262"
  }, {
    "timestamp" : "2022-06-08T03:36:00Z",
    "value" : "10926"
  }, {
    "timestamp" : "2022-06-08T03:37:00Z",
    "value" : "10271"
  }, {
    "timestamp" : "2022-06-08T03:38:00Z",
    "value" : "10870"
  } ],
  "color" : "#80BEBF"
}

Leave a Comment