Bug: ZRender event handlers not working properly after update (1.4.0 > 1.4.2)
Description
After updating from 1.4.0 to the latest version (1.4.2), attaching event handlers directly to the ZRender instance using getZr() throws an error when trying to cleanup on unmount.
Previous Behavior
useEffect(() => {
echartsInstance?.getZr().on('mousedown', () => {
console.log('=> mousedown')
})
return () => {
echartsInstance?.getZr().off('mousedown')
}
}, [echartsInstance])
This code worked as expected.
Current Behavior
Now throws error:
Uncaught TypeError: Cannot read properties of null(reading 'off')
Root Cause
This might be related to the new event cleanup implementation in event-handlers.ts, causing the ZRender instance to become temporarily invalid during the cleanup phase.
Solution
While the previous implementation worked on the version 1.4.0, a more defensive approach should be used by storing and validating the ZRender reference before using it:
useEffect(() => {
const zr = echartsInstance?.getZr()
if (!zr) return
zr.on('mousedown', () => setShowTooltip(true))
return () => zr.off('mousedown')
}, [echartsInstance])
Link to Reproduction
https://stackblitz.com/edit/vitejs-vite-fndic4en?file=src%2Fcomponents%2FChart.tsx
Steps to reproduce
- Navigate to a page with the ECharts component
- Navigate away from this page
- You will see a blank page (Check the console for the error)
JS Framework
React (TS)
Version
1.4.2
Browser
Google Chrome 132.0.6834.160
Operating System
- [x] macOS
- [ ] Windows
- [ ] Linux
Additional Information
This might not be considered an actual bug but I wanted to share it anyway so that if anybody goes through the same issue they will have a solution.
edit: Ha, I just realised my comment is just a repeat of what you already wrote. So, re-fetching ZRender was working previously, but has regressed?
(my original comment follows)
This seems to be related to attempting to fetch the instance of ZRender again after the component is unmounted. I was able to get the code working by just storing the instance in a const:
import { useEffect } from 'react';
import { useECharts } from '@kbox-labs/react-echarts';
import { options } from '../chartOptions';
export const Chart = () => {
const [containerRef, echartsInstance] = useECharts<HTMLDivElement>({
...options,
});
useEffect(() => {
if (!echartsInstance) return;
const zRender = echartsInstance.getZr();
zRender.on('mousedown', () => {
console.log('=> mousedown 🙃');
});
return () => {
zRender.off('mousedown');
};
}, [echartsInstance]);
return <div ref={containerRef} style={{ height: '400px', width: '100%' }} />;
};
edit: Ha, I just realised my comment is just a repeat of what you already wrote. So, re-fetching ZRender was working previously, but has regressed?
Correct. As I mentioned above, I got it working after storing the instance, but it broke my application after the update, so I thought a good idea to raise this issue anyway so that if anybody goes through the same problem they will have a solution.