apexcharts / react-apexcharts

📊 React Component for ApexCharts
https://apexcharts.com
MIT License
1.31k stars 158 forks source link

Updating State of one chart re-render all charts #336

Closed YuriLopesM closed 2 months ago

YuriLopesM commented 3 years ago

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

prabureddy commented 3 years ago

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

YuriLopesM commented 3 years ago

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

junedchhipa commented 3 years ago

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

YuriLopesM commented 3 years ago

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)

elie222 commented 3 years ago

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.

YuriLopesM commented 3 years ago

@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 commented 3 years ago

@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 commented 3 years ago

Any updates here @junedchhipa?

Nsj94 commented 2 years ago

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

saadahmsiddiqui commented 1 year ago

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

david-mart commented 1 year ago

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 commented 1 year ago

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 };
};
lordphnx commented 1 year ago

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

a11smiles commented 1 year ago

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.

Javeedhussiain commented 1 year ago

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

saadahmsiddiqui commented 8 months ago

memoizing the entire component itself doesn't seem efficient

github-actions[bot] commented 2 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Creative-Difficulty commented 2 months ago

why is this auto-closed?