Skip to content

How to parse new Date.now() object with D3.js to generate a linechart

I’m trying to make a linechart with D3 and React where the x axis is based on Date.now() object and all the ticks are a minute apart on a 10mn window.

  1. I can’t generate the line because I get “NaNNaNNaN” in my svg path;
  2. Can’t seem to figure out how to have ticks minutes apart on my x axis;

Here’s how the data looks like

// data state
data = [
{"loadAverage":0.008333333333333333,"timestamp":1632740462342},
{"loadAverage":0.008333333333333333,"timestamp":1632740459323},
{"loadAverage":0.013333333333333334,"timestamp":1632740471400}
];

the timestamp key is a new Date.now() coming from the server

useEffect(() => {
const svg = d3.select(d3Container.current);

let margin = { top: 20, right: 20, bottom: 50, left: 70 },
 width = 960 - margin.left - margin.right,
 height = 500 - margin.top - margin.bottom;

// set the ranges
let x = d3
 .scaleTime()
 .domain(d3.extent(data, (d) => timeFormat(d.timestamp)))
 .range([0, width]);

let y = d3
 .scaleLinear()
 .domain([0, d3.max(data, (d) => d.loadAverage)])
 .nice()
 .range([height, 0]);

// Parse the date
let parseTime = d3.timeParse("%s");
let timeFormat = d3.timeFormat("%M:%S");

// Constructing the line
const myLine = d3
 .line()
 .x((d) => {
   const convertedTime = parseTime(d.timestamp);
   return x(convertedTime);
 })
  .y((d) => {
  return y(d.loadAverage);
});

svg
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg
.select("svg")
.selectAll("path")
.data([data])
.join("path")
.attr("d", (value) => myLine(value))
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round");

// Add the x Axis
svg
 .select("svg")
 .attr("transform", "translate(0," + height + ")")
 .call(d3.axisBottom(x));

// Add the y Axis
svg
 .select("svg")
 .append("g")
 .call(d3.axisLeft(y).tickFormat(timeFormat).ticks(10));
}, [data]);

This is my first time using D3, any help would be greatly appreciated !

Edit: here’s what I tried so far

// Constructing the line
const myLine = d3
 .line()
 .x((d) => {
   const convertedTime = new Date(d.timestamp);
   return x(convertedTime);
 })
  .y((d) => {
  return y(d.loadAverage);
});

Even tried to return convertedTime wrapped up by parsetime like so parsetime(convertedTime) Didn’t work either.

Answer

I think you have a problem in Initializing x scale domain

// set the ranges
let x = d3
 .scaleTime()
  //                              ⬇️ here is the issue, just get rid of timeFormat
 .domain(d3.extent(data, (d) => timeFormat(d.timestamp)))
 .range([0, width]);

the scaleTime expect the domain to be a [Date|number, Date|number], you are using timeFormat which convert number|Date into a string based on the given format.

Try to use this instead:

 let x = d3
      .scaleTime()
      .domain(d3.extent(data, (d) => d.timestamp))
      .range([0, width]);

  // The short vesion

 let x = d3.scaleTime(d3.extent(data, (d) => d.timestamp), [0, width])

Constructing the line

const myLine = d3
 .line()
 .x((d) => x(d.timestamp))
 .y((d) => y(d.loadAverage));

If you need to convert timestamps into Dates, you can map the whole data array

data = data.map(d=> d.timestamp = new Date(d.timestamp), d)