@@ -15,6 +15,7 @@ const state = {
1515 height : '100%' ,
1616}
1717
18+ // 注册地图数据
1819echarts . registerMap ( 'china' , china as unknown as GeoJSONCompressed )
1920
2021const EChartsCommon = ( props : {
@@ -26,77 +27,151 @@ const EChartsCommon = (props: {
2627} ) => {
2728 const drawDomRef = useRef < HTMLDivElement > ( null )
2829 const chartRef = useRef < EChartsType | null > ( null )
30+ const isInitializedRef = useRef ( false )
31+ const resizeObserverRef = useRef < ResizeObserver | null > ( null )
2932
30- const dispose = ( ) => {
31- if ( ! chartRef . current ) {
32- return
33+ // 安全地销毁图表实例
34+ const dispose = useCallback ( ( ) => {
35+ if ( chartRef . current ) {
36+ try {
37+ chartRef . current . dispose ( )
38+ } catch ( error ) {
39+ console . error ( 'ECharts dispose error:' , error )
40+ }
41+ chartRef . current = null
42+ isInitializedRef . current = false
43+ }
44+ } , [ ] )
45+
46+ // 使用 ResizeObserver 替代 window.resize 事件
47+ const setupResizeObserver = useCallback ( ( ) => {
48+ if ( ! drawDomRef . current || ! chartRef . current ) return
49+
50+ // 清理旧的观察者
51+ if ( resizeObserverRef . current ) {
52+ resizeObserverRef . current . disconnect ( )
3353 }
34- chartRef . current . dispose ( )
35- chartRef . current = null
36- }
3754
38- const resize = debounce ( ( ) => {
39- chartRef ?. current ?. resize ( )
40- } , 100 )
55+ // 创建新的观察者
56+ resizeObserverRef . current = new ResizeObserver (
57+ debounce ( ( ) => {
58+ if ( chartRef . current ) {
59+ try {
60+ chartRef . current . resize ( )
61+ } catch ( error ) {
62+ console . error ( 'ECharts resize error:' , error )
63+ }
64+ }
65+ } , 100 )
66+ )
4167
68+ resizeObserverRef . current . observe ( drawDomRef . current )
69+ } , [ ] )
70+
71+ // 安全地设置图表配置
4272 const setOption = useCallback (
4373 ( option : OptionType ) => {
4474 if ( ! chartRef . current ) {
75+ console . warn ( 'ECharts instance is not available' )
4576 return
4677 }
47- const { notMerge } = props
48- const { lazyUpdate } = props
49- chartRef . current . setOption ( option , notMerge , lazyUpdate )
78+
79+ try {
80+ const { notMerge = false , lazyUpdate = false } = props
81+ chartRef . current . setOption ( option , notMerge , lazyUpdate )
82+ } catch ( error ) {
83+ console . error ( 'ECharts setOption error:' , error )
84+ // 尝试重新初始化图表
85+ dispose ( )
86+ initChart ( drawDomRef . current )
87+ }
88+ } ,
89+ [ props . notMerge , props . lazyUpdate , dispose ]
90+ )
91+
92+ // 初始化图表
93+ const initChart = useCallback (
94+ ( dom : HTMLDivElement | null ) => {
95+ if ( ! dom || chartRef . current ) return
96+
97+ try {
98+ // 确保 DOM 元素有尺寸
99+ if ( dom . clientWidth === 0 || dom . clientHeight === 0 ) {
100+ console . warn ( 'Chart container has zero size, delaying initialization' )
101+ return
102+ }
103+
104+ const renderer = props . renderer || 'canvas'
105+ chartRef . current = echarts . init ( dom , null , {
106+ renderer,
107+ width : 'auto' ,
108+ height : 'auto' ,
109+ } )
110+
111+ isInitializedRef . current = true
112+
113+ // 执行初始化回调
114+ if ( props . instanceHandle ) {
115+ props . instanceHandle ( chartRef . current )
116+ }
117+
118+ // 设置初始配置
119+ setOption ( props . option )
120+
121+ // 设置尺寸观察
122+ setupResizeObserver ( )
123+ } catch ( error ) {
124+ console . error ( 'ECharts initialization error:' , error )
125+ chartRef . current = null
126+ isInitializedRef . current = false
127+ }
50128 } ,
51- // eslint-disable-next-line react-hooks/exhaustive-deps
52- [ props . notMerge , props . lazyUpdate ]
129+ [ props , setOption , setupResizeObserver ]
53130 )
54131
55- // 初始化组件
56- const initChart = ( dom : HTMLDivElement | null ) => {
57- if ( chartRef . current ) return
58- if ( ! dom ) return
59- // renderer 用于配置渲染方式 可以是 svg 或者 canvas
60- const renderer = props . renderer || 'canvas'
61- chartRef . current = echarts . init ( dom , null , {
62- renderer,
63- width : 'auto' ,
64- height : 'auto' ,
65- } )
66- // 执行初始化的任务,例如注册地图
67- if ( props . instanceHandle ) props . instanceHandle ( chartRef . current )
68- setOption ( props . option )
69- // 监听屏幕缩放,重新绘制 echart 图表
70- window . addEventListener ( 'resize' , resize )
71- }
72-
73- const initHandle = ( ) => {
74- // 还没实例走初始化
75- if ( ! chartRef . current ) {
132+ // 处理图表初始化或更新
133+ const initHandle = useCallback ( ( ) => {
134+ if ( ! chartRef . current || ! isInitializedRef . current ) {
76135 initChart ( drawDomRef . current )
77136 } else {
78137 setOption ( props . option )
79138 }
80- }
81-
82- useEffect (
83- ( ) =>
84- // 组件卸载
85- ( ) => {
86- window . removeEventListener ( 'resize' , resize )
87- dispose ( )
88- } ,
89- // eslint-disable-next-line react-hooks/exhaustive-deps
90- [ ]
91- )
139+ } , [ initChart , setOption , props . option ] )
92140
93- // 每次更新组件都重置
141+ // 组件挂载和卸载
94142 useEffect ( ( ) => {
95143 initHandle ( )
96- // eslint-disable-next-line react-hooks/exhaustive-deps
97- } , [ props . option ] )
98144
99- return < div className = "default-chart" ref = { drawDomRef } style = { { width : state . width , height : state . height } } />
145+ return ( ) => {
146+ // 清理 ResizeObserver
147+ if ( resizeObserverRef . current ) {
148+ resizeObserverRef . current . disconnect ( )
149+ resizeObserverRef . current = null
150+ }
151+
152+ // 销毁图表实例
153+ dispose ( )
154+ }
155+ } , [ initHandle , dispose ] )
156+
157+ // 当 option 变化时更新图表
158+ useEffect ( ( ) => {
159+ if ( isInitializedRef . current ) {
160+ setOption ( props . option )
161+ }
162+ } , [ props . option , setOption ] )
163+
164+ return (
165+ < div
166+ className = "default-chart"
167+ ref = { drawDomRef }
168+ style = { {
169+ width : state . width ,
170+ height : state . height ,
171+ minHeight : '200px' , // 确保容器有最小高度
172+ } }
173+ />
174+ )
100175}
101176
102177export default EChartsCommon
0 commit comments