封装ECharts Next For React

起因是因为想用ECharts Next (v5) 版本, 而echarts-for-react目前支持到v4,查看了一下其源码,决定自己封装一下,来完成业务开发。

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import type { FC } from 'react';
import React, { useEffect, useRef } from 'react';
import type { EChartsOption } from 'echarts';
import { dispose, getInstanceByDom, init } from 'echarts';
import classNames from 'classnames';
import isFunction from 'lodash/isFunction';
import isEmpty from 'lodash/isEmpty';
import isString from 'lodash/isString';
import { bind, clear } from 'size-sensor';
import { useMount, useUnmount } from 'react-use';
import 'echarts-gl';

export type EChartsNextForReactCoreProps = {
option: EChartsOption;
notMerge?: boolean;
replaceMerge?: string | string[];
lazyUpdate?: boolean;
silent?: boolean;
style?: React.CSSProperties;
className?: string;
theme?: string | Record<string, unknown>;
opts?: {
renderer?: 'canvas' | 'svg';
devicePixelRatio?: number;
width?: number;
height?: number;
locale?: string | any;
};
showLoading?: boolean;
onChartReady?: (arg0: any) => void;
onEvents?: Record<string, unknown>;
};

const EChartsNextForReactCore: FC<EChartsNextForReactCoreProps> = (props) => {
const {
style,
className,
theme,
opts,
option,
notMerge,
lazyUpdate,
showLoading,
onChartReady,
onEvents,
} = props;
const chartRefElements = useRef<HTMLDivElement>(null);

const getChartInstance = () => {
if (chartRefElements?.current) {
return (
getInstanceByDom(chartRefElements?.current) ?? init(chartRefElements?.current, theme, opts)
);
}
return undefined;
};

const getChartDom = () => {
const chartObj = getChartInstance();
if (chartObj) {
chartObj.setOption(option, notMerge ?? false, !!lazyUpdate ?? false);
if (showLoading) {
chartObj.showLoading();
} else {
chartObj.hideLoading();
}
return chartObj;
}
return undefined;
};

const bindEvents = () => {
const chartElms = getChartDom();
if (!isEmpty(onEvents) && chartElms) {
// @ts-ignore
// eslint-disable-next-line no-restricted-syntax
for (const [key, func] of Object.entries(onEvents)) {
if (
Object.prototype.hasOwnProperty.call(onEvents, key) &&
isString(key) &&
isFunction(func)
) {
chartElms.on(key, (param: any) => {
func(param, chartElms);
});
}
}
}
};

const renderChart = () => {
const chartObj = getChartDom();
bindEvents();
if (chartObj) {
if (isFunction(onChartReady)) {
onChartReady(chartObj);
} else if (chartRefElements) {
bind(chartRefElements?.current, () => {
try {
chartObj?.resize();
} catch (e) {
console.error(e);
}
});
}
}
};

const disposeCurrentChart = () => {
if (chartRefElements?.current) {
clear(chartRefElements?.current);
dispose(chartRefElements?.current);
}
};

useMount(() => {
renderChart();
});

useEffect(() => {
disposeCurrentChart();
renderChart();
}, [theme, opts, onEvents]);

useEffect(() => {
const chartDom = getChartDom();
// @ts-ignore
if (chartDom?._dom) {
// @ts-ignore
bind(chartDom._dom, () => {
try {
chartDom?.resize();
} catch (e) {
console.error(e);
}
});
}
}, [style, className, showLoading]);

useUnmount(() => {
disposeCurrentChart();
});

return (
<div
ref={chartRefElements}
style={{
height: 360,
...style,
}}
className={classNames('echarts-next-for-react', className)}
/>
);
};

EChartsNextForReactCore.defaultProps = {
notMerge: false,
lazyUpdate: false,
showLoading: false,
};

export default EChartsNextForReactCore;

使用

1
2
3
npm install echarts-next-for-react -s

yarn add echarts-next-for-react

封装过程中遇到的问题

在最初的一版时,能够正常显示,加入了lazyUpdate属性之后,发现Chart直接崩掉,报错。

经过排查发现是因为,bind dom事件时,直接使用了

1
const chartRefElements = useRef<HTMLDivElement>(null);

返回的对象,而不是其真实的dom对象

1
2
3
4
5
6
useEffect(() => {
const chartDom = getChartDom();
if (chartDom) {
chartDom.resize();
}
}, [style, className, showLoading]);

改成了

1
2
3
4
5
6
7
8
9
10
11
12
useEffect(() => {
const chartDom = getChartDom();
if (chartDom?._dom) {
bind(chartDom._dom, () => {
try {
chartDom?.resize();
} catch (e) {
console.error(e);
}
});
}
}, [style, className, showLoading]);

这样之后,就可以完整运行了,并且可以实现事件的绑定等操作。

总结:在使用开源软件的时候,与其在等别人开发不如积极主动的参与。