react-apexcharts icon indicating copy to clipboard operation
react-apexcharts copied to clipboard

Updating State of one chart re-render all charts

Open YuriLopesM opened this issue 3 years ago • 16 comments

Context and the problem

I'm testing the library using the wrapper of React with Next.js (for fetching data with Server-Side Rendering). I pass some fake data with props to the component, and then initializing the state with the props (only for the Bar Chart, the others I use directly the props). When I update the Bar Chart with 'setBarData', all componentes re-draw even if I only change one chart.

I took this example from the repository as a basis. I'm doing something wrong or this is related to Next.js?

Code

const [barData, setBarData] = useState<IChart>(barChart); // The props of SSR

function updateBarChartData() {
    let data: number[] = [];
    
    // Random data just for update
    for (let i = 0; i < 8; i++) { 
        data.push(Math.floor(Math.random() * 100))
    }
    
    setBarData(prevState => {
        return {
            options: { ...prevState.options },
            series: [
                {
                    name: "series-1",
                    data: data
                }
                
            ]
        }
    })
}

Screen view

issue_charts

YuriLopesM avatar Aug 31 '21 13:08 YuriLopesM

Even I have the same issue... Any solutions?

prabureddy avatar Sep 05 '21 15:09 prabureddy

Nothing on my side. Seems to be a problem with ApexCharts. Hope someone who maintains this library can help us 😢.

YuriLopesM avatar Sep 06 '21 15:09 YuriLopesM

This shouldn't happen. There must be some configuration issue. Can you please post a CodeSandBox reproduction so I can look into it closely?

junedchhipa avatar Sep 13 '21 17:09 junedchhipa

Yes definitely. I'll do that until tomorrow, I have a lot of work for today, but I'm glad you answered (I was worried because I really want to use this lib in my projects)

YuriLopesM avatar Sep 14 '21 19:09 YuriLopesM

I also came across this. If you don't change the state for the right chart it should be okay. You can probably find a way around it using React.Memo and friends. If React doesn't rerender the right chart component then nothing should happen to it even if things change on the left.

elie222 avatar Sep 17 '21 00:09 elie222

@junedchhipa sorry for the delay in my reply! Unfortunately, I couldn't create a demo on CodeSandBox. I tried but doesn't work 😢.

I created a branch in the repository I was working on, I was using it to test libraries (cookies, themes, etc). In this branch I left only the Cookies page, removed the login logic and kept the same package.json. I think you can test with this, I'm here for help if needed.

Link for the branch Link for the chart page

I left only one button for changing themes, which may be the one interfering (when I change the theme, the graphics are rendered again, but I think this was expected). I know the logic is a little wrong and I fixed it on another system using SSR.

YuriLopesM avatar Sep 17 '21 12:09 YuriLopesM

@elie222 I'll try it that way, but I guess it wasn't expected to do the re-rendering by updating just one graphic, it doesn't make sense. It might be something I'm doing wrong, but it can help anyone who has encountered this barrier as well. Maybe if only useMemo can do this, it's probably good to update the documents. I'll wait for Juned's response as I said, but it's a good way to handle the error using memoization.

YuriLopesM avatar Sep 17 '21 12:09 YuriLopesM

Any updates here @junedchhipa?

YuriLopesM avatar Sep 23 '21 13:09 YuriLopesM

I am having same issue . Any one resolved it ??? Please UPDATE

Nsj94 avatar Sep 29 '22 15:09 Nsj94

I would like to request devs to change string heavy comparison to some heuristics based approach, large string comparisons offer poor performance

saadahmsiddiqui avatar Dec 03 '22 05:12 saadahmsiddiqui

So apparently this happens when you pass chart attribute in the options object. If you remove it and just use ReactApexChart props to set those values everything works all right. The props don't expose all the possible values, but you can pass still pass them in.

This makes all the charts update randomly:

  // DOESNT WORK
  //------------
  const options: ApexCharts.ApexOptions = {
    //...
    chart: {
     //...
      id: "candles",
      group: "mainchart",
      animations: {
        enabled: false,
      },
    },
   // ...
  };
  return (
    <ReactApexChart
      options={options}
      series={series}
      type="candlestick"
      height={350}
      width="100%"
    />
  );

This works as expected:


 // WORKS
 // ----------
  const options: ApexCharts.ApexOptions = {
    //...
    /// REMOVE chart:{}
   //...
  };
  return (
    <ReactApexChart
      options={options}
      series={series}
      type="candlestick"
      height={350}
      width="100%"
      id="candles"
      group="mainchart"
      animations={{
        enabled: false,
      }}
    />
  );

Of course, you can just use props spread like <ReactApexChart {...chart} />

david-mart avatar Jan 10 '23 04:01 david-mart

Also, if someone is interested, moving away from this massive react lib with this simple hook made my life much easier:

import { useEffect, useRef } from "react";
import ApexCharts from "apexcharts";

export const useApexChart = (options: ApexCharts.ApexOptions) => {
  const elRef = useRef<HTMLDivElement>(null);
  const chartRef = useRef<ApexCharts>();
  const prevOptions = useRef<ApexCharts.ApexOptions>();

  useEffect(() => {
    chartRef.current = new ApexCharts(elRef.current!, options);
    chartRef.current.render();
    prevOptions.current = options;
    return () => {
      chartRef.current?.destroy();
    };
  }, []);

  useEffect(() => {
    const { chart, ...opts } = options!;
    chartRef.current?.updateOptions(opts);
  }, [options]);

  return { elRef };
};

david-mart avatar Jan 10 '23 04:01 david-mart

@david-mart I'm intrigued by this solution, could you also paste an example of how you use the hook itself?

lordphnx avatar Jul 22 '23 09:07 lordphnx

None of the solutions worked for me. However, I used the useMemo hook and worked like a charm.

export default function Chart({ ...props }: Prop) {
  /*
    Cut out extra code for brevity
  */

  const chart = React.useMemo(
    () => (
      <ReactApexChart
        css={hideTooltips}
        options={options}
        series={series}
        type="line"
        height="100%"
        ref={ref}
      />
    ),
    [counts]
  );

  return <>{chart}</>;
}

Now it only updates when its data (counts) updates.

a11smiles avatar Oct 05 '23 16:10 a11smiles

None of the solutions worked for me. However, I used the useMemo hook and worked like a charm.

export default function Chart({ ...props }: Prop) {
  /*
    Cut out extra code for brevity
  */

  const chart = React.useMemo(
    () => (
      <ReactApexChart
        css={hideTooltips}
        options={options}
        series={series}
        type="line"
        height="100%"
        ref={ref}
      />
    ),
    [counts]
  );

  return <>{chart}</>;
}

Now it only updates when its data (counts) updates.

would you provide me full code

Javeedhussiain avatar Oct 16 '23 12:10 Javeedhussiain

memoizing the entire component itself doesn't seem efficient

saadahmsiddiqui avatar Mar 13 '24 12:03 saadahmsiddiqui