Skip to content

Commit a099328

Browse files
committed
fix rerender and dimension issues
1 parent 816e0ee commit a099328

File tree

1 file changed

+123
-58
lines changed

1 file changed

+123
-58
lines changed

src/components/ChartUPlot.tsx

Lines changed: 123 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, {
2+
useState,
23
forwardRef,
34
useCallback,
45
useImperativeHandle,
@@ -101,23 +102,28 @@ function _sliceSeries(
101102
*/
102103
function getDimensions(
103104
options: any,
104-
style: any,
105-
deviceWidth: number,
106-
deviceHeight: number,
105+
width: number,
106+
height: number,
107+
// style: any,
108+
// deviceWidth: number,
109+
// deviceHeight: number,
107110
margin: { title?: number; legend?: number },
108111
): {
109112
optionsFinal: any;
110-
containerWidth: number;
111-
containerHeight: number;
113+
// containerWidth: number;
114+
// containerHeight: number;
112115
} {
113-
var containerWidth = options?.width || style?.width || deviceWidth;
114-
containerWidth = Math.min(containerWidth, deviceWidth);
116+
// var containerWidth = options?.width || style?.width || deviceWidth;
117+
// containerWidth = Math.min(containerWidth, deviceWidth);
115118

116-
var containerHeight = options?.height || style?.height || deviceHeight;
117-
containerHeight = Math.min(containerHeight, deviceHeight);
119+
// var containerHeight = options?.height || style?.height || deviceHeight;
120+
// containerHeight = Math.min(containerHeight, deviceHeight);
118121

119-
var uplotWidth = containerWidth;
120-
var uplotHeight = containerHeight;
122+
// var uplotWidth = containerWidth;
123+
// var uplotHeight = containerHeight;
124+
125+
var uplotWidth = width;
126+
var uplotHeight = height;
121127

122128
if (options?.title) {
123129
// Subtract height for title
@@ -135,11 +141,12 @@ function getDimensions(
135141
optionsFinal.width = uplotWidth;
136142
optionsFinal.height = uplotHeight;
137143

138-
return { optionsFinal, containerWidth, containerHeight };
144+
return optionsFinal;
145+
// return { optionsFinal, containerWidth, containerHeight };
139146
}
140147

141148
function getCreateChartString(
142-
data: number[][] | null,
149+
data: number[][] | null = null,
143150
options: any,
144151
bgColor: string = 'transparent',
145152
injectedJavaScript: string = '',
@@ -152,7 +159,7 @@ function getCreateChartString(
152159
data !== null ? `if (window.__CHART_CREATED__) return;` : ``;
153160

154161
return `
155-
(function() {
162+
(function() {
156163
${chartCreatedCheck}
157164
window.__CHART_CREATED__ = true;
158165
@@ -217,21 +224,50 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
217224
let webref: any = useRef(null);
218225
const uplotInstance = useRef<any>(null);
219226
const dataRef = useRef<number[][]>(data as number[][]);
220-
const initialized = useRef(false);
227+
const variablesRef = useRef<{ [key: string]: any }>({});
228+
const initialized = useRef<boolean>(false);
229+
const containerRef = useRef<any>(null);
230+
const dimensionsRef = useRef({
231+
containerWidth: options?.width || style?.width || height,
232+
containerHeight: options?.height || style?.height || width,
233+
});
221234

222235
const bgColor = style?.backgroundColor || 'transparent';
223236

224-
var { optionsFinal, containerWidth, containerHeight } = useMemo(() => {
225-
return getDimensions(options, style, width, height, margin);
226-
}, [options, style, width, height]);
237+
const handleLayout = useCallback((event) => {
238+
const { width, height } = event.nativeEvent.layout;
239+
dimensionsRef.current = {
240+
containerWidth: width,
241+
containerHeight: height,
242+
};
243+
}, []);
244+
245+
// var { optionsFinal, containerWidth, containerHeight } = useMemo(() => {
246+
247+
// const { containerWidth, containerHeight } = dimensionsRef.current.containerWidth
248+
// ? dimensionsRef.current
249+
// : { containerWidth: width, containerHeight: height };
250+
251+
// return getDimensions(options, style, containerWidth, containerHeight, margin);
252+
// }, [style, width, height]);
253+
254+
// var optsFinal = useMemo(() => {
255+
// return options
256+
// // return getDimensions(
257+
// // options,
258+
// // dimensionsRef.current.containerWidth,
259+
// // dimensionsRef.current.containerHeight,
260+
// // margin,
261+
// // );
262+
// }, [style, margin]);
227263

228264
useEffect(() => {
229265
// update uplot height and width if options change
230266

231267
if (isWeb) {
232268
uplotInstance.current?.setSize({
233-
width: containerWidth,
234-
height: containerHeight,
269+
width: dimensionsRef.current.containerWidth,
270+
height: dimensionsRef.current.containerHeight,
235271
});
236272
} else {
237273
if (!webref.current) {
@@ -241,24 +277,39 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
241277

242278
webref.current.injectJavaScript(`
243279
if (window._chart) {
244-
window._chart.setSize(${JSON.stringify(containerWidth)}, ${JSON.stringify(containerHeight)});
280+
window._chart.setSize(${JSON.stringify(dimensionsRef.current.containerWidth)}, ${JSON.stringify(dimensionsRef.current.containerHeight)});
245281
} else {
246282
console.error('Chart not initialized');
247283
}
248284
true;
249285
`);
250286
}
251-
}, [containerWidth, containerHeight]);
287+
}, [
288+
dimensionsRef.current.containerHeight,
289+
dimensionsRef.current.containerWidth,
290+
]);
252291

253292
// eslint-disable-next-line @typescript-eslint/no-explicit-any
254293
const createChart = useCallback(
255-
(opts: any, data: number[][], bgColor?: string): void => {
294+
(opts: any, data: number[][] | null = null, bgColor?: string): void => {
256295
if (initialized.current) {
257296
return;
258297
}
259298

299+
const optsFinal = getDimensions(
300+
opts,
301+
// style,
302+
dimensionsRef.current.containerWidth,
303+
dimensionsRef.current.containerHeight,
304+
margin,
305+
);
306+
260307
if (isWeb) {
261-
uplotInstance.current = new uPlot(opts, data, webref.current);
308+
uplotInstance.current = new uPlot(
309+
optsFinal,
310+
data == null ? dataRef.current : data,
311+
webref.current,
312+
);
262313
} else {
263314
// inject background color before chart setup if provided
264315
if (!webref?.current) {
@@ -267,7 +318,7 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
267318
}
268319

269320
webref.current.injectJavaScript(
270-
getCreateChartString(data, opts, bgColor),
321+
getCreateChartString(data, optsFinal, bgColor),
271322
);
272323
}
273324
initialized.current = true;
@@ -282,24 +333,10 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
282333
* @param {Object} newOptions - The new options to set for the chart.
283334
*/
284335
const updateOptions = useCallback((newOptions: any): void => {
285-
if (isWeb) {
286-
if (uplotInstance.current) {
287-
uplotInstance.current.destroy();
288-
}
336+
// call getDimensions to update optionsFinal
289337

290-
uplotInstance.current = new uPlot(
291-
newOptions,
292-
dataRef.current,
293-
webref.current,
294-
);
295-
} else {
296-
if (!webref?.current) {
297-
console.error('WebView reference is not set');
298-
return;
299-
}
300-
301-
webref.current.injectJavaScript(getCreateChartString(null, newOptions));
302-
}
338+
destroy(true); // keep data
339+
createChart(newOptions);
303340
}, []);
304341

305342
/**
@@ -374,7 +411,6 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
374411
window._data[i].push(item[i]);
375412
}
376413
if (window._chart) {
377-
console.debug('Pushing new data to uPlot chart');
378414
window._chart.setData(window._data);
379415
} else {
380416
console.error('Chart not initialized');
@@ -455,6 +491,8 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
455491
// if web, sets the variable to window.[name]
456492
// if native, sets the variable to window.[name] via webref.current.injectJavaScript
457493
const setVariable = useCallback((name: string, value: any): void => {
494+
variablesRef.current[name] = value;
495+
458496
if (isWeb) {
459497
if (!window) {
460498
console.error('Window is not defined');
@@ -469,11 +507,7 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
469507
}
470508

471509
webref.current.injectJavaScript(`
472-
if (window._chart) {
473-
window.${name} = ${JSON.stringify(value)};
474-
} else {
475-
console.error('Chart not initialized');
476-
}
510+
window.${name} = ${JSON.stringify(value)};
477511
true;
478512
`);
479513
}
@@ -501,23 +535,27 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
501535
}, []);
502536

503537
// function to call destroy, also clears the data
504-
const destroy = useCallback((): void => {
538+
const destroy = useCallback((keepData: boolean = false): void => {
505539
if (isWeb) {
506540
uplotInstance.current?.destroy();
507-
dataRef.current = [];
541+
if (!keepData) {
542+
dataRef.current = [];
543+
}
508544
} else {
509545
if (!webref?.current) {
510546
console.error('WebView reference is not set');
511547
return;
512548
}
513549

550+
var keepDataStr = keepData ? '' : `window._data = [];`;
551+
514552
webref.current.injectJavaScript(`
515-
window._data = []
553+
${keepDataStr}
516554
517555
if (window._chart) {
518556
window._chart.destroy();
519-
window.__CHART_CREATED__ = false;
520557
}
558+
window.__CHART_CREATED__ = false;
521559
true;
522560
`);
523561
}
@@ -550,15 +588,17 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
550588
return (
551589
<View
552590
ref={(r): any => {
591+
containerRef.current = r;
553592
webref.current = r;
554593
if (r) {
555-
createChart(optionsFinal, data);
594+
createChart(options, data);
556595
}
557596
}}
597+
onLayout={handleLayout}
558598
style={{
559599
...style,
560-
width: containerWidth,
561-
height: containerHeight,
600+
width: options?.width || style?.width || '100%',
601+
height: options?.height || style?.height || '100%',
562602
}}
563603
/>
564604
);
@@ -570,22 +610,47 @@ const ChartUPlot = forwardRef<any, UPlotProps>(
570610
source={html}
571611
style={{
572612
...style,
573-
width: containerWidth,
574-
height: containerHeight,
613+
width: options?.width || style?.width || '100%',
614+
height: options?.height || style?.height || '100%',
575615
}}
576616
scrollEnabled={false}
577617
onLoadEnd={(): void => {
578-
createChart(optionsFinal, data, bgColor);
618+
createChart(options, data, bgColor);
579619

580620
if (onLoad) {
581621
onLoad();
582622
}
583623
}}
584624
ref={(r) => {
585625
if (r) {
626+
var shouldReinit = containerRef.current && r !== webref.current;
627+
628+
if (shouldReinit) {
629+
initialized.current = false;
630+
destroy(true);
631+
}
632+
633+
containerRef.current = r;
586634
webref.current = r;
635+
636+
if (shouldReinit) {
637+
// re-add any variables that were set
638+
var injectedVars = '';
639+
Object.keys(variablesRef.current).forEach((key) => {
640+
injectedVars += `window.${key} = ${JSON.stringify(
641+
variablesRef.current[key],
642+
)};`;
643+
});
644+
webref.current.injectJavaScript(`
645+
${injectedVars}
646+
true;
647+
`);
648+
649+
createChart(options, data, bgColor);
650+
}
587651
}
588652
}}
653+
onLayout={handleLayout}
589654
javaScriptEnabled={true}
590655
injectedJavaScript={`${injectedJavaScript}; true;`}
591656
onMessage={(payload): void => {

0 commit comments

Comments
 (0)