From f992fbf6b77fbc081920b530032ddfd2f71bdc21 Mon Sep 17 00:00:00 2001
From: dangreen <danon0404@gmail.com>
Date: Tue, 26 Oct 2021 15:26:43 +0700
Subject: [PATCH] fix: data updating fix

#806
---
 src/chart.tsx         | 16 ++++++++++-----
 src/utils.ts          | 41 +++++++++++++++++++++++++++++++++++++--
 stories/Chart.data.ts | 45 +++++++++++++++++++++++++++++++++++++++++++
 stories/Chart.tsx     | 16 ++++++++++++++-
 4 files changed, 110 insertions(+), 8 deletions(-)

diff --git a/src/chart.tsx b/src/chart.tsx
index 357890a5e..754178902 100644
--- a/src/chart.tsx
+++ b/src/chart.tsx
@@ -4,7 +4,13 @@ import ChartJS from 'chart.js/auto';
 import type { ChartData, ChartType, DefaultDataPoint } from 'chart.js';
 
 import type { Props, TypedChartComponent } from './types';
-import { reforwardRef, setNextDatasets } from './utils';
+import {
+  reforwardRef,
+  cloneData,
+  setOptions,
+  setLabels,
+  setDatasets,
+} from './utils';
 
 const noopData = {
   datasets: [],
@@ -49,7 +55,7 @@ function ChartComponent<
 
     chartRef.current = new ChartJS(canvasRef.current, {
       type,
-      data,
+      data: cloneData(data),
       options,
       plugins,
     });
@@ -119,19 +125,19 @@ function ChartComponent<
 
   useEffect(() => {
     if (!redraw && chartRef.current && options) {
-      chartRef.current.options = { ...options };
+      setOptions(chartRef.current, options);
     }
   }, [redraw, options]);
 
   useEffect(() => {
     if (!redraw && chartRef.current) {
-      chartRef.current.config.data.labels = data.labels;
+      setLabels(chartRef.current.config.data, data.labels);
     }
   }, [redraw, data.labels]);
 
   useEffect(() => {
     if (!redraw && chartRef.current && data.datasets) {
-      setNextDatasets(chartRef.current.config.data, data.datasets);
+      setDatasets(chartRef.current.config.data, data.datasets);
     }
   }, [redraw, data.datasets]);
 
diff --git a/src/utils.ts b/src/utils.ts
index 3b27bc49f..dd0303120 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -4,6 +4,8 @@ import type {
   ChartData,
   DefaultDataPoint,
   ChartDataset,
+  ChartOptions,
+  Chart,
 } from 'chart.js';
 
 export function reforwardRef<T>(ref: ForwardedRef<T>, value: T) {
@@ -14,7 +16,26 @@ export function reforwardRef<T>(ref: ForwardedRef<T>, value: T) {
   }
 }
 
-export function setNextDatasets<
+export function setOptions<
+  TType extends ChartType = ChartType,
+  TData = DefaultDataPoint<TType>,
+  TLabel = unknown
+>(chart: Chart<TType, TData, TLabel>, nextOptions: ChartOptions<TType>) {
+  chart.options = { ...nextOptions };
+}
+
+export function setLabels<
+  TType extends ChartType = ChartType,
+  TData = DefaultDataPoint<TType>,
+  TLabel = unknown
+>(
+  currentData: ChartData<TType, TData, TLabel>,
+  nextLabels: TLabel[] | undefined
+) {
+  currentData.labels = nextLabels;
+}
+
+export function setDatasets<
   TType extends ChartType = ChartType,
   TData = DefaultDataPoint<TType>,
   TLabel = unknown
@@ -30,10 +51,26 @@ export function setNextDatasets<
     );
 
     // There is no original to update, so simply add new one
-    if (!currentDataset || !nextDataset.data) return nextDataset;
+    if (!currentDataset || !nextDataset.data) return { ...nextDataset };
 
     Object.assign(currentDataset, nextDataset);
 
     return currentDataset;
   });
 }
+
+export function cloneData<
+  TType extends ChartType = ChartType,
+  TData = DefaultDataPoint<TType>,
+  TLabel = unknown
+>(data: ChartData<TType, TData, TLabel>) {
+  const nextData: ChartData<TType, TData, TLabel> = {
+    labels: [],
+    datasets: [],
+  };
+
+  setLabels(nextData, data.labels);
+  setDatasets(nextData, data.datasets);
+
+  return nextData;
+}
diff --git a/stories/Chart.data.ts b/stories/Chart.data.ts
index 43e1cccfc..4b8dc7f22 100644
--- a/stories/Chart.data.ts
+++ b/stories/Chart.data.ts
@@ -108,3 +108,48 @@ export const eventsData = {
     },
   ],
 };
+
+export const sameData1 = {
+  labels: [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec',
+  ],
+  datasets: [
+    {
+      label: 'My First dataset',
+      backgroundColor: 'rgba(75,192,192,0.4)',
+      data: [33, 53, 85, 41, 44, 65, 61, 47, 52, 53, 62, 82],
+    },
+    {
+      label: 'My Second dataset',
+      backgroundColor: '#742774',
+      data: [33, 25, 35, 51, 54, 76, 65, 40, 42, 39, 51, 55],
+    },
+  ],
+};
+
+export const sameData2 = {
+  labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
+  datasets: [
+    {
+      label: 'My First dataset',
+      backgroundColor: 'rgba(75,192,192,0.4)',
+      data: [42, 13, 45, 29, 44, 25, 27],
+    },
+    {
+      label: 'My Second dataset',
+      backgroundColor: '#742774',
+      data: [33, 25, 35, 44, 50, 40, 48],
+    },
+  ],
+};
diff --git a/stories/Chart.tsx b/stories/Chart.tsx
index 01c401d71..b3a975d26 100644
--- a/stories/Chart.tsx
+++ b/stories/Chart.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useReducer } from 'react';
 import { InteractionItem } from 'chart.js';
 import Chart from '../src';
 import * as data from './Chart.data';
@@ -100,3 +100,17 @@ Redraw.args = {
   data: data.multiTypeData,
   redraw: true,
 };
+
+export const SameDataToggle = args => {
+  const [currentData, toggleData] = useReducer(
+    prevState =>
+      prevState === data.sameData1 ? data.sameData2 : data.sameData1,
+    data.sameData1
+  );
+
+  return <Chart {...args} data={currentData} onClick={toggleData} />;
+};
+
+SameDataToggle.args = {
+  type: 'bar',
+};