Search code examples
reactjsplotlyplotly.js

React.plotly.js re-draws graph/component when data point is changed


I'm using REACT-PLOTLY.JS to create a scatter graph. I've got everything working apart from the graph redrawing it self every-time I change a data point when using a list of objects in the array data prop. BUT... when I manually write the array and it's containing objects, my graph does not re-draw when I change a data point, which is how it should work. The 2nd solution is not dynamic and is unusable. Can anyone help please?

re-draws graph when data point changes.

<Plot
                data={plotlyData}

does not re-draw graph when data point is changed but is not dynamic and therefore unusable.

<Plot
                data={[plotlyData[0],plotlyData[1]]}

I'm using functional components.

How plotData is generated. I'm using an API to get the coordinates for the X and Y axis.

import { React, useState } from "react";
import Plot from "react-plotly.js";
import { useQuery } from "react-query";
import axios from "axios";

const PlotlyGraph = (props) => {
    const [plot, setPlot] = useState([]);///not in use
    const { datasets } = props;

    const QueryUuid = datasets.map((d) => {
        // console.log("Individual Dataset:", d);
        return `${d.age}-${d.kmax}-${d.frontK}-${d.pachymetry}`;
    });
    const { error, isLoading, data } = useQuery(
        `data-${QueryUuid.join("-")}`,
        () =>
            axios.post("/api/calculate_cxl", {
                age_baseline: datasets.map((d) => d.age),
                kmax: datasets.map((d) => d.kmax),
                front_k1: datasets.map((d) => d.frontK),
                tpt2: datasets.map((d) => d.pachymetry),
                color: datasets.map((d) => d.color),
            })
    );

    let plotlyData;
    if (error !== null || isLoading === true) {
        plotlyData = [];
    } else {
        plotlyData = data.data.map((d, i) => {
            return {
                x: d.x,
                y: d.y,
                type: "scatter",
                mode: "lines",
                marker: { color: datasets[i].color },
                line: {
                    width: 3,
                },

                name: `Patient ${i + 1}`,
                showlegend: true,
            };
        });
    }

    console.log("plot:", plotlyData);

    //- Graph Configuration
    const config = {
        editable: false,
        scrollZoom: true,
        displayModeBar: true,
        displaylogo: false,
    };

    return (
        <>
            <Plot
                data={plotlyData}
                layout={{
                    yaxis: { range: [0, 1] },
                    xaxis: { range: [0, 5] },
                    autoSize: "true",
                    title: "Patient Comparison",
                }}
                style={{ width: "100%", height: " 700px" }}
                useResizeHandler={true}
                config={config}
                revision={0}
                // onInitialized={plot}
                // onUpdate={(plot) => setPlot(plotlyData)}
            />
        </>
    );
};

export default PlotlyGraph;

Solution

  • I had a similar issue and was able to figure out how to dynamically update my plot with new data after every API fetch (see State Management). Since I don't have access to the API you are using, I've included my own example.

    From my API, I fetch an object that looks like this:

    {
      28AD7D49F6E13C0A: [69.36, 64.11, 68.69, 62.1, ...],
      28DDC649F6003C1C: [69.59, 63.18, 60.63, 63.08, ...],
      Time: ['20:50:15', '20:50:17', '20:50:19', '20:50:21', ...]
    }
    

    Every two seconds, that objects gets updated with a new item in each array. When the state data gets updated with setData, the state object gets updated, which then causes the plot to render with new data.

    Full example:

    import React, { useState, useEffect } from "react";
    import Plot from "react-plotly.js";
    
    function TemperatureGraph() {
      const [data, setData] = useState({});
    
      const state = {
        data: [
          {
            x: data["Time"],
            y: data["28AD7D49F6E13C0A"],
            name: "28AD7D49F6E13C0A",
            type: "scatter",
            mode: "lines+markers",
            marker: {
              color: "red",
            },
          },
          {
            x: data["Time"],
            y: data["28DDC649F6003C1C"],
            name: "28DDC649F6003C1C",
            type: "scatter",
            mode: "lines+markers",
            marker: {
              color: "black",
            },
          },
        ],
        layout: {
          width: 800,
          height: 500,
          title: "",
          xaxis: {
            title: "Time",
          },
          yaxis: {
            title: "Temperature (F)",
          },
        },
        frames: [],
        config: {},
      };
    
      useEffect(() => {
        const timer = setInterval(() => {
          fetch("/get-temperatures")
            .then((response) => response.json())
            .then((data) => setData(data));
        }, 2000);
        return () => clearInterval(timer);
      }, []);
    
      return (
        <div>
          <Plot data={state.data} layout={state.layout} />
        </div>
      );
    }
    
    export default TemperatureGraph;