Home > Back-end >  React/Javascript - saving values from local csv into useState / useEffect
React/Javascript - saving values from local csv into useState / useEffect

Time:10-05

I'm trying to load the data from a csv into useState via a useEffect state. But I can't get it to work.

My csv contains data like this:

  Date             Value1   Value2 
  2020/7/20        1        4              
  2020/7/21        2        8 
  2020/7/22        3        10
  2020/7/23        4        12
  2020/7/24        5        67
  2020/7/25        6        10

And I'm trying to get the data in a useState via useEffect like this:

import * as d3 from 'd3';
import {useEffect, useState} from 'react';
import csvFile from '../data/Data.csv';


const Linechart = (props) => {

  const [data, setData] = useState([]); 

  useEffect(()=>{
     if (data.length > 0) {
       drawChart();
     } else {

      getCSVData();
    }
   ,[]);

    // gets csv data from csv
  const getCSVData = async () => {
    let tempData = [];
      await d3.csv(csvFile,

      function(d){
        tempData.push({date: d3.timeParse("%Y/%m/%d")(d.Date), value1: parseFloat(d.Value1), value2: parseFloat(d.Value2)}),    
      } 
    )
      setData(tempData);

      console.log("data is: ", data.date)
      console.log("value1 is: ", data.value1)
      console.log("value2 is: ", data.value2)
  }

For some reason it does not want to work. I get the following error:

attr.js:30 Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNL…".

It seems to get stuck at tempData.push. I have never used this .push (I'm very new to both React and Javascript). Can someone help me fix this maybe?

The data is used for a d3 visualization, here is the full code to create the linechart:

import * as d3 from 'd3';
import {useEffect, useState} from 'react';
 import csvFile from '../data/Data.csv';


const Linechart = (props) => {

  const [data, setData] = useState([]);

    const {width, height } = props;

  useEffect(()=>{
    if (data.length > 0) {
      drawChart();
    } else {

      getCSVData();
    }},[data]);

   console.log(data)

    // gets csv data from a csv
  const getCSVData = async () => {
    let tempData = [];
      await d3.csv(csvFile,

      function(d){

        tempData.push({date: d3.timeParse("%m/%d/%Y")(d.Date), value1: parseFloat(d.Value1), value2: parseFloat(d.Value2)});
      }

    )
    console.log(tempData)
      setData(tempData);
      console.log("data is: ",data.date)
  }

const drawChart = () => {

    // create the chart area
    const svg = d3.select('.svg-canvas')
  svg.selectAll("*").remove();

// Add X axis --> it is a date format
    var x = d3.scaleTime()
      .domain(d3.csv(data, function(d) { return d.date; }))
      .range([ 0, width ]);
      
    svg.append("g")
      .attr("transform", "translate(0,"   height   ")")
      .call(d3.axisBottom(x));


    // Add Y axis
    var y = d3.scaleLinear()
      .domain([0, d3.max(data, function(d) { return   d.value1; })])
      .range([ height, 0 ]);
    svg.append("g")
      .call(d3.axisLeft(y));

    // set line coordinates
    const line = d3.line()
      .x(function(d) { return x(d.date) })
      
      .y(function(d) { return y(d.value1) })

    // Add the line
    svg.append("path")
      .datum(data)
      .attr("fill", "none")
      .attr("stroke", "steelblue")
      .attr("stroke-width", 1.5)
      .attr("d", line)
 }

return (
    <div>
     <svg className="svg-canvas" width="1000px" height="600px" />


    </div>
    )

}

export default Linechart;

The error comes from the drawChart function as it is not receiving the right data

CodePudding user response:

You can not use .push() like that, you need to wrap all the values in a object and then push them to the tempData array. After that you will be able to set the state with the new array.

tempData.push( { date: d3.timeParse("%Y/%m/%d")(d.Date), value1: parseFloat(d.Value1), value2: parseFloat(d.Value2) } );
setData(tempData);  

Now you will have state which is going to be array with one item on the 0 index which is going to be object with properties.

[ 0: { date: ... , value1: ... , value2: ... } ]

After that when you need to access your data you have to loop over the array.

data.map((currentEl) => {
  console.log(currentEl)
  console.log(currentEl.date)
  console.log(currentEl.value1)
  console.log(currentEl.value2)
})

Another way yo do this is to set your state to object,

const [data, setData] = useState({date: '', value1: '', value2: ''});  

const tempData = { date: d3.timeParse("%Y/%m/%d")(d.Date), value1: parseFloat(d.Value1), value2: parseFloat(d.Value2) }  

setData(tempData);  
console.log(data.date)
console.log(data.value1)
console.log(data.value2)

CodePudding user response:

You have an issue with both your data and how you are parsing it.

  1. The data isn't comma delineated.
  2. The date format you are trying to use has the units in reverse order and the dates are not zero-padded.

The data should actually use comma separated columns:

Data.csv

Date,Value1,Value2
2020/7/20,1,4
2020/7/21,2,8
2020/7/22,3,10
2020/7/23,4,12
2020/7/24,5,67
2020/7/25,6,10

Ensure there is also no trailing whitespace at the end of each line.

The format you should use to parse the date is "%Y/%m/%d".

const getCSVData = async () => {
  const tempData = [];

  await d3.csv(
    csvFile,
    function (d) {
      tempData.push({
        date: d3.timeParse("%Y/%m/%d")(d.Date),
        value1: Number(d.Value1),
        value2: Number(d.Value2)
      });
    }
  );

  setData(tempData);
};

Edit react-javascript-saving-values-from-local-csv-into-usestate-useeffect

enter image description here

  • Related