javascriptreactjscanvasjs

CanvasJS chart not updating on first page load


I am developing a web application where I want to present data in the form of a line graph. For this I am using the CanvasJS component CanvasJSChart. Everything works well except that the graph is not loaded when I try to reload a page. This is what I see when I reload the page. When I then update the code (insert a new line and save) I see this. The datetime fields are used to filter which dates and times that should be visible and they work as long as the data points are visible when I edit them. The only thing I have found that solves this is by updating the code or resizing the window.

This is the code that I use to create the options and return the chart component.

import React from 'react';
import CanvasJSReact from '@canvasjs/react-charts';

const options = {
    zoomEnabled: true,
    animationEnabled: true,
    animationDuration: 1200,
    title: {
        text: "Graph to see shit"
    },

    toolTip: {
        shared: true,
        contentFormatter: function(e){
            var content = " ";
            for (var i = 0; i < e.entries.length; i++) {
                content += e.entries[i].dataSeries.name 
                        + " " + "<strong>" 
                        + e.entries[i].dataPoint.y + "</strong>";
                content += "<br/>";
            }
            content += e.entries[0].dataPoint.x + "<br/>";
            return content
        }
    },

    data: [
    {
        type: "line",
        name: "Temperature",
        showInLegend: true,
        dataPoints: []
    },
    {
        type: "line",
        name: "Humidity",
        showInLegend: true,
        dataPoints: []
    }]
}

export function GraphFunc(props){

    options.data[0].dataPoints = props.data.tempPoints;
    options.data[1].dataPoints = props.data.humPoints;
    return(
    <div>
        < CanvasJSReact.CanvasJSChart options={options} />
    </div>
    )
}

This is the code that calls the API, collects datapoints, handle the date pickers and creates the chart component.

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import dayjs from 'dayjs';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { GraphFunc } from './GraphFunc.js'

function formatData(data, lower, upper){
  var tmp = {tempPoints: [], humPoints: []};

  data.forEach(element => {
    var date = new Date(element.date);
    
    if(lower != undefined && upper != undefined){
      var epochTime = date.getTime();
      
      if(upper < epochTime || epochTime < lower){
        return;
      }
    }

    tmp.tempPoints.push({y: Number(element.temp), x: date})
    tmp.humPoints.push({y: Number(element.hum), x: date})
  });
  console.log("Nu byter jag saker")
  return tmp;
}

export function Graph() {

  const [data, getData] = useState('')
  const [startDate, setStartValue] = useState(dayjs());
  const [endDate, setEndValue] = useState(dayjs());
  const [oldStart, setOldStart] = useState(dayjs());
  const [oldEnd, setOldEnd] = useState(dayjs());
  
  useEffect(() => {
    getAllData(undefined, undefined);
  }, []);

  const getAllData = (lower, upper) => {
    axios.get("/api/dhts/")
        .then((res) => {
          const d = formatData(res.data, lower, upper)
          getData(d);
        })
        .catch((err) => console.log(err));
  }

  function handleChanges(e){
    if(startDate.$d.getTime() == oldStart.$d.getTime() && endDate.$d.getTime() == oldEnd.$d.getTime()){
      return
    }
    
    setOldStart(startDate);
    setOldEnd(endDate);

    getAllData(startDate.$d.getTime(), endDate.$d.getTime());
  }


  return(
    <>
    <GraphFunc data={data} />

    <div style={{display: "flex", alignItems: "center", justifyContent: "center",}}>
        <strong style={{marginBottom: 7, marginTop: 7}}>Pick dates for displaying data</strong>
    </div>  

    <LocalizationProvider dateAdapter={AdapterDayjs}>
      <div style={{display: "flex", alignItems: "center", justifyContent: "center",}}>
          <DateTimePicker value={startDate} onChange={setStartValue} onClose={handleChanges} label="Start date" />
          <DateTimePicker value={endDate} onChange={setEndValue} onClose={handleChanges} label="End date" />
      </div>  
    </LocalizationProvider>  
    </>
  )
}

I found this post which had similar issues but our programs look quite different. I do think that something is asynchronous and that causes this to not load. However, I don't have a clue what could fix it. Is anyone sitting on a quick fix for this?


Solution

  • Try to make the graphFunc like this, I think ur problem is in the first render.

    The data is not here yet but react render the component anyways, and no re-renders to make it work again.

    But when u change in code, this is actually a re-render so the DOM updated with the data

    import React from 'react';
    import CanvasJSReact from '@canvasjs/react-charts';
    
    
    
    export function GraphFunc(props){
    
        const options = {
        zoomEnabled: true,
        animationEnabled: true,
        animationDuration: 1200,
        title: {
            text: "Graph to see shit"
        },
    
        toolTip: {
            shared: true,
            contentFormatter: function(e){
                var content = " ";
                for (var i = 0; i < e.entries.length; i++) {
                    content += e.entries[i].dataSeries.name 
                            + " " + "<strong>" 
                            + e.entries[i].dataPoint.y + "</strong>";
                    content += "<br/>";
                }
                content += e.entries[0].dataPoint.x + "<br/>";
                return content
            }
        },
    
        data: [
        {
            type: "line",
            name: "Temperature",
            showInLegend: true,
            dataPoints: props.data?.tempPoints
        },
        {
            type: "line",
            name: "Humidity",
            showInLegend: true,
            dataPoints: props.data?.humPoints
        }]
    }
    
        if (!props.data.humPoints || !props.data.tempPoints) return "Loading";
        return(
        <div>
            <CanvasJSReact.CanvasJSChart options={options} />
        </div>
        )
    }