Streamgraph with React

Im trying to rebuild this streamgraph with react.

However when I want to generate the paths, I get NaNs.
I think that the problem is how I call the area function, but im not sure what to do.

import React, { Component } from "react";
import "./App.css";
import {
  stack,
  area,
  curveBasis,
  stackOrderInsideOut,
  stackOffsetSilhouette,
} from "d3-shape";
import { schemeDark2 } from "d3-scale-chromatic";
import { range } from "d3-array";
import { scaleLinear, scaleOrdinal } from "d3-scale";
import { extent } from "d3-array";


export const StreamGraph = ({ data, width, height }) => {
  const keys = data.columns.slice(1)

  // Color for each category
  const colorScale = scaleOrdinal()
    .domain(keys)
    .range(schemeDark2);
  

  // Accessor function to get the year and then build scale from it
  const xValue = (d) => d.year;
  const xScale = scaleLinear()
    .domain(extent(data, xValue))
    .range([0, width]);

  const yScale = scaleLinear().domain([-100000, 100000]).range([height, 0]);

  // could do some filtering here
  const stackData = data
  
  
  const stackLayout = stack()
    .offset(stackOffsetSilhouette)
    .order(stackOrderInsideOut)
    .keys(keys)

  // Using the stackLayout we get two additional components for y0 and y1.
  // For x we want to get the yeaer from the original data, so we have to access d.data
  const stackArea = area()
    .x((d) => xScale(d.data.year))
    .y0((d) => yScale(d[0]))
    .y1((d) => yScale(d[1]))
    .curve(curveBasis);


  // Generate path elements
  const stacks = stackLayout(stackData).map((d, i) => {
    console.log(d)
    return (
    <path
      key={"stack" + i}
      d={stackArea(d)}
      style={{
        fill: colorScale(d.key),
        stroke: "black",
        strokeOpacity: 0.25,
      }}
    />
  )});

  return (
    <svg width={width} height={height}>
      <g>{stacks}</g>
    </svg>
  );
};

and the script that loads the data:

import {useState, useEffect} from "react";
import { csv } from "d3-fetch";


const url = "https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/5_OneCatSevNumOrdered_wide.csv"

const row = d => {
    d.Amanda = +d["Amanda"]
    d.Ashley = +d["Ashley"]
    d.Betty = +d["Betty"]
    d.Deborah = +d["Deborah"]
    d.Dorothy = +d["Dorothy"]
    d.Helen = +d["Helen"]
    d.Linda = +d["Linda"]
    d.Patricia = +d["Patricia"]
    d.year = +d["year"]
    return d;
}

export const useData = () => {
    const [data, setData] = useState(null);
    useEffect(() => {
        // Call d3.csv using row function as accessor
        csv(url, row).then(setData);
    }, []);
    return data;
}

I edited the code and this now works.

My mistake was that I called d.year in area().x(), but needed to access d.data.year, since the stackLayout changed my data structure.

If anyone has suggestions, please comment :slight_smile:

And if anybody is looking for a basic streamgraph template in react, here you go.

Oh nice! If your work is in VizHub, can you please share the link?

Here’s a StreamGraph I worked on:

I could imagine wrapping an implementation like that in a React component using the useRef and useEffect hooks for the integration.

I figured it out!

I just finished your 2021 course so looking at pure d3 code was pretty daunting at first. However I’ve been practicing by porting d3 templates to the d3 + react style that you teached in the course (loved it by the way).

Here is my final product, any suggestions?

Also Greetings from Tatiana L. Im currently doing my Masterthesis for her :wink:

EDIT: Here is a Treemap template that I converted. I also left a code snippet in it where useRef is used. So if somebody wants to study how to transfer d3 code to d3 + useRef or pure React, you can check out this Treemap:

Beautiful! Very nice work!

I’m a huge fan of labels inside the streamgraph itself. I would suggest to add labels to the streamgraph with this thing I made years ago:

However, looking at your code, I see that using this would require a refactor such that the DOM manipulation for the paths be done in D3 not React, because (unfortunately) the d3-area-label library only works if the DOM manipulation for the paths is done in D3.

Another thing I’m a huge fan of for streamgraphs is smoothing, because it removes the jagged noise we see in your example, while retaining the overall patterns, making the whole thing more readable.

But as a bare bones template for the StreamGraph technique, I think what you’ve created is perfect.

Say hello to Tatiana for me :wink: and all the best with your thesis work!