Skip to content

Commit 5bb8584

Browse files
committed
copy input data to avoid mutation issues, clean up
1 parent 19c2628 commit 5bb8584

File tree

1 file changed

+53
-26
lines changed

1 file changed

+53
-26
lines changed

src/components/ChartUPlot.tsx

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ function isTypedArray(x: unknown): x is AnyTypedArray {
109109
*/
110110
function toPlainArrays(arrays: any[]): any[] {
111111
return arrays.map((arr) =>
112-
isTypedArray(arr) ? Array.from(arr as ArrayLike<number | bigint>) : arr,
112+
isTypedArray(arr)
113+
? Array.from(arr as ArrayLike<number | bigint>)
114+
: arr.slice(),
113115
);
114116
}
115117

@@ -165,18 +167,7 @@ function getDimensions(
165167
margin: { title?: number; legend?: number },
166168
): {
167169
optionsFinal: any;
168-
// containerWidth: number;
169-
// containerHeight: number;
170170
} {
171-
// var containerWidth = options?.width || style?.width || deviceWidth;
172-
// containerWidth = Math.min(containerWidth, deviceWidth);
173-
174-
// var containerHeight = options?.height || style?.height || deviceHeight;
175-
// containerHeight = Math.min(containerHeight, deviceHeight);
176-
177-
// var uplotWidth = containerWidth;
178-
// var uplotHeight = containerHeight;
179-
180171
var uplotWidth = width;
181172
var uplotHeight = height;
182173

@@ -197,7 +188,6 @@ function getDimensions(
197188
optionsFinal.height = uplotHeight;
198189

199190
return optionsFinal;
200-
// return { optionsFinal, containerWidth, containerHeight };
201191
}
202192

203193
function getCreateChartString(
@@ -337,23 +327,36 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
337327
const { width, height } = useWindowDimensions();
338328
let webref: any = useRef(null);
339329
const uplotInstance = useRef<any>(null);
340-
const dataRef = useRef<number[][]>(data as number[][]);
330+
// Ensure we keep an independent copy of the incoming `data` so modifications
331+
// to dataRef.current do NOT mutate the original prop. toPlainArrays converts
332+
// typed arrays into plain arrays and returns new arrays.
333+
const dataRef = useRef<number[][]>(
334+
toPlainArrays((data || []) as any[]) as number[][],
335+
);
341336
const variablesRef = useRef<{ [key: string]: any }>({});
342337
const initialized = useRef<boolean>(false);
343338
const containerRef = useRef<any>(null);
344339
const dimensionsRef = useRef({
345-
containerWidth: Math.round(options?.width || style?.width || height),
346-
containerHeight: Math.round(options?.height || style?.height || width),
340+
containerWidth: Math.round(options?.width || style?.width || width),
341+
containerHeight: Math.round(options?.height || style?.height || height),
347342
});
348343

349344
const bgColor = style?.backgroundColor || 'transparent';
350345

351346
const handleLayout = useCallback((event) => {
352347
const { width, height } = event.nativeEvent.layout;
348+
// console.log(
349+
// `handleLayout | name=${name}, width=${width}, height=${height}`,
350+
// );
351+
353352
dimensionsRef.current = {
354353
containerWidth: Math.round(width),
355354
containerHeight: Math.round(height),
356355
};
356+
357+
if (isWeb) {
358+
handleLoadEnd();
359+
}
357360
}, []);
358361

359362
// memoized webview/view style to avoid recreating object on every render
@@ -368,6 +371,8 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
368371

369372
// memoized onLoadEnd handler for native WebView
370373
const handleLoadEnd = useCallback((): void => {
374+
// console.log(`handleLoadEnd | name=${name}`);
375+
371376
// Use canonical dataRef when creating the native WebView chart
372377
dataRef.current = toPlainArrays(data as any[]) as number[][];
373378
createChart(options, dataRef.current, bgColor);
@@ -417,8 +422,7 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
417422
if (!r) return;
418423

419424
// On web, simply create chart when the DOM node appears
420-
if (Platform.OS === 'web') {
421-
createChart(options, data);
425+
if (isWeb) {
422426
return;
423427
}
424428

@@ -450,6 +454,16 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
450454
[options, data, bgColor],
451455
);
452456

457+
// useEffect(() => {
458+
// console.log('useffect [ref]', ref);
459+
460+
// if (Platform.OS === 'web') {
461+
// console.log('useffect [ref] - success');
462+
// handleLoadEnd();
463+
// return;
464+
// }
465+
// }, [ref]);
466+
453467
// Keep canonical copy of incoming prop `data` (convert typed arrays to plain arrays).
454468
// Also mirror to window._data for native platforms when webref is available.
455469
useEffect(() => {
@@ -515,7 +529,7 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
515529
return;
516530
}
517531

518-
const optsFinal = getDimensions(
532+
const optsFinal: any = getDimensions(
519533
opts,
520534
// style,
521535
dimensionsRef.current.containerWidth,
@@ -768,32 +782,45 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
768782

769783
// function to call destroy, also clears the data
770784
const destroy = useCallback((keepData: boolean = false): void => {
771-
// console.log(`destroy | name=${name}, keepData=${keepData}`);
785+
// console.log(
786+
// `destroy | name=${name}, keepData=${keepData}, data=${data?.length}`,
787+
// );
788+
789+
if (!keepData) {
790+
dataRef.current = [];
791+
}
772792

773793
if (isWeb) {
774794
uplotInstance.current?.destroy();
775-
if (!keepData) {
776-
dataRef.current = [];
777-
}
778795
} else {
779796
if (!webref?.current) {
780797
console.error('WebView reference is not set');
781798
return;
782799
}
783800

784801
var keepDataStr = keepData ? '' : `window._data = [];`;
802+
// var dataStr = data ? `window._data = ${JSON.stringify(data)};` : '';
785803

786804
const body = `
787805
${keepDataStr}
788-
if (window._chart) {
789-
try { window._chart.destroy(); } catch (e) {}
806+
807+
try {
808+
window._chart.setData(window._data || []);
809+
window._chart.destroy();
810+
} catch (e) {
811+
console.error('destroy | could not destroy chart');
790812
}
813+
791814
window.__CHART_CREATED__ = false;
815+
792816
// clear queued ops to avoid stale calls after destroy
793817
window.__uplot_queue__ = [];
818+
819+
true;
794820
`;
821+
795822
// run immediately (no need to queue)
796-
webref.current.injectJavaScript(`${body} true;`);
823+
webref.current.injectJavaScript(body);
797824
}
798825
initialized.current = false;
799826
}, []);

0 commit comments

Comments
 (0)