In today's tech landscape, React, Angular, and Vue stand out as the most widely recognized front-end development libraries or frameworks. When developing user interfaces, developers choose these libraries or frameworks based on specific project requirements. Similarly, when designing dashboards, developers seek out visualization libraries to help streamline development efforts.
When developing dashboard interfaces in particular, seamless interaction synchronization, such as sharing mouse events and zoom functionality across multiple charts, is essential. Finding visualization libraries for these dashboard interfaces is quite challenging, especially when they must integrate well within other UI libraries or frameworks.
In this article, I describe this complex scenario that synchronizes multiple instances of ECharts line charts that are encapsulated with individual React components within a rendering list.
We use the Apache ECharts sample data, Rainfall vs Evaporation, which includes time series data for rainfall and evaporation for 3 different regions (Region A, Region B and Region C).
My implementation
The typical main steps for implementing this scenario are as follows:
Create one React component RainfallEvaporationLineChart to wrap the line chart of ECharts.
Create one rendering list of the React component RainfallEvaporationLineChart for the 3 different regions.
Synchronize the charts in the React component rendering list.
Implementing steps 1 and 2 is relatively straightforward. However, step 3 presents numerous challenges and issues, as outlined in the next section.
Challenges encountered
Apache ECharts provides one connect method that connects the interaction of multiple chart series, but you need to know the ECharts instance first.
The eharts-for-react library provides one getEchartsInstance() API to get the ECharts instance object, but you need to know the React referencing values of ReactECharts component using refs. The API works very well when the refs are predefined; however, in our scenario, predefining refs is not feasible.
React Refs come with several limitations:
You cannot call useRef in a loop, in a condition, or inside a map() call due to Hooks must only be called at the top-level of React component. React offers two formal solutions to address the issue. First, you can create a single ref to the parent element and traverse down to the individual child nodes. Or, you can pass a callback function to the ref attribute, enabling the maintenance of a custom refs array or map. See How to manage a list of refs using a ref callback for more detailed information.
You cannot directly use a ref to access DOM nodes of another React component. Attempting to do so will result in the warning: "Function components cannot be given refs. Attempts to access this ref will fail."React.forwardRef() is the recommended solution provided by React. See Accessing another components dom nodes for more detailed information.
The ref.current value may constantly change because "React sets ref.current during the commit. Before updating the DOM, React sets the affected ref.current values to null. After updating the DOM, React immediately sets them to the corresponding DOM nodes. Usually, you will access refs from event handlers." See When React attaches the refs for more detailed information.
The primary challenge here lies in obtaining the ECharts instance object, which proves to be difficult using React refs and ensuring smooth functionality.
Implemented solution
Upon examining the component properties of the echarts-for-react library, you can see the onChartReady property, which is a callback function that receives the ECharts object as its parameter and executes when the chart is ready. So, you can use this callback to address the issue and implement the desired feature.
Let's look at the main functions in the App.tsx file:
...
functionApp() {
// Set sample data
...
// Set group id to connect the created chartsconst groupId = 'rainfall-evaporation-line-chart-group-id'// Set Echarts instannce const echartsInstanceStore = {} asRecord<string, EChartsInstance>;
// Callback function for echarts onChartReady eventfunctiononChartReadyCallback(instance: EChartsInstance) {
// Set group id and add new echarts instance to the storeif (instance) {
if (!echartsInstanceStore[instance.id]) {
instance.group = groupId;
echartsInstanceStore[instance.id] = instance;
}
}
// Remove echarts instance when it is nullfor (const [key, value] ofObject.entries(echartsInstanceStore)) {
if (!value) {
delete echartsInstanceStore[key];
}
}
// Enable interaction synchronization for multiple chartsif (Object.entries(echartsInstanceStore).length > 1) {
echarts.disconnect(groupId);
echarts.connect(groupId);
}
}
// Create the rendering list for chartsconst charts = regionData.map((region, i) => {
return<Colsm={12}md={12}lg={6}xl={6}xxl={6}key={i}><Cardstyle={{margin: '16px', padding: '8px'}}><Card.Body><Card.Title>Rainfall vs Evaportation in {region}</Card.Title><RainfallEvaporationLineChartdata={{'time':timeData,
'rainfall':rainfallData[i],
'evaporation':evaporationData[i]}}
onChartReady={onChartReadyCallback}></RainfallEvaporationLineChart></Card.Body></Card></Col>;
}
);
return (
<Container><Row>
{charts}
</Row></Container>
);
}
Show more
Results and outcomes
The three charts for the three regions look as follows:
The following animation illustrates the final results and outcomes:
Summary and next steps
This article demonstrates how to synchronize multiple ECharts charts encapsulated with individual React components within a rendering list in a simple way.
You can learn more about React and Echarts from these guides:
About cookies on this siteOur websites require some cookies to function properly (required). In addition, other cookies may be used with your consent to analyze site usage, improve the user experience and for advertising.For more information, please review your cookie preferences options. By visiting our website, you agree to our processing of information as described in IBM’sprivacy statement. To provide a smooth navigation, your cookie preferences will be shared across the IBM web domains listed here.