While dealing with scatter plots, bar charts, etc., it is common to run into requirements wherein you need to perform some data-transformation prior to rendering any visualization. For instance, you may want to sort all the bars in your bar chart in an ascending order, showcase linear regression in your scatter plot, or filter data points belonging to a certain date range. Fortunately, Apache ECharts supports some of these use-cases out-of-the-box.
However, native support for 2 important (IMO) transformations is still missing: regression (in scatter plot) and group-and-aggregate (in bar chart). Since Apache ECharts supports custom data-transforms as well, we were fortunate to find third-party transformation plugins for both of our needs: echarts-simple-transform and echarts-stat. ✨
echarts-simple-transform plugin, however, isn't that well maintained currently and has TypeScript compatibility issues as well. We addressed the shortcomings of this plugin in a new fork and published it as: @manufac/echarts-simple-transform.
This article shall delve into how to use @manufac/echarts-simple-transform with TypeScript & React.
Prerequisites
You may want to look into 2 of our previous articles first, to obtain a better understanding of how to integrate ECharts with TypeScript and React:
- Using Apache ECharts with React and TypeScript
- Using Apache ECharts with React and TypeScript: Optimizing Bundle Size
Once you have understood how to integrate Apache ECharts with React and TypeScript, and how the bundle optmization workflow/syntax looks like, the rest of the article shall be much easier to grasp.
Some Key Notes
- You need to register an external data transform first in order to use it. This is the only differentiating thing about the source code. The rest of the source code can be implemented as mentioned in the prerequisite articles.
import { registerTransform, ... } from "echarts/core";
import { aggregate } from "@manufac/echarts-simple-transform";
import type { ExternalDataTransform } from "@manufac/echarts-simple-transform";
registerTransform(aggregate as ExternalDataTransform);
The
as
assertion inas ExternalDataTransform
is needed due to a type mismatch error. Since@manufac/echarts-simple-transform
exportsExternalDataTransform
, the type mismatch error is easily dealt with usingas
assertion. This isn't possible withecharts-simple-transform
since it doesn't export any types and you may have to resort to@ts-ignore
there.We understand that using
as
assertion may not be an ideal fix but efforts were taken to refactor some types/interfaces present insideecharts-simple-transform
such thatas
assertion isn't needed. After trying out various alternates, we speculate that some changes are needed in theecharts
project itself to update theregisterTransform
function typings so that it can easily accommodate external/third-party transforms.echarts-simple-transform
has an issue with correctly aggregating (summing up) single element groups.@manufac/echarts-simple-transform
overcomes that shortcoming as well. Apart from this bug fix, all of the remaining functionality is kept unchanged.More differences between the two variants are listed in our project README.
How to use the aggregate
transform?
Here's the complete recipe:
import { aggregate } from "@manufac/echarts-simple-transform";
import { BarChart } from "echarts/charts";
import { TransformComponent } from "echarts/components";
import { init, getInstanceByDom, use, registerTransform } from "echarts/core";
import { useRef, useEffect } from "react";
import type { ExternalDataTransform } from "@manufac/echarts-simple-transform";
import type { BarSeriesOption } from "echarts/charts";
import type { ECharts, ComposeOption, SetOptionOpts } from "echarts/core";
// Register the required components
use([
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
LegendComponent,
ToolboxComponent,
CanvasRenderer,
BarChart,
TransformComponent, // Built-in transform (filter, sort)
]);
registerTransform(aggregate as ExternalDataTransform); // `as` assertion is needed
export interface BarChartProps {
style?: CSSProperties;
settings?: SetOptionOpts;
loading?: boolean;
theme?: "light" | "dark";
option: ComposeOption<TitleComponentOption | TooltipComponentOption | GridComponentOption | DatasetComponentOption | BarSeriesOption>;
}
export function BarChart({
option,
style,
settings,
loading,
theme,
}: BarChartProps): JSX.Element {
const chartRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Initialize chart
let chart: ECharts | undefined;
if (chartRef.current !== null) {
chart = init(chartRef.current, theme);
}
// Add chart resize listener
// ResizeObserver is leading to a bit janky UX
function resizeChart() {
chart?.resize();
}
window.addEventListener("resize", resizeChart);
// Return cleanup function
return () => {
chart?.dispose();
window.removeEventListener("resize", resizeChart);
};
}, [theme]);
useEffect(() => {
// Update chart
if (chartRef.current !== null) {
const chart = getInstanceByDom(chartRef.current);
chart?.setOption(option, settings);
}
}, [option, settings, theme]);
useEffect(() => {
// Update chart
if (chartRef.current !== null) {
const chart = getInstanceByDom(chartRef.current);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
loading === true ? chart?.showLoading() : chart?.hideLoading();
}
}, [loading, theme]);
return (
<div ref={chartRef} style={{ width: "100%", height: "100px", ...style }} />
);
}
Finally, the option
object can be constructed as given in this documentation.
How to implement a custom data transform?
If you have any use cases which are aren't covered by any of the existing plugins, you could consider implementing one on your own. We couldn't locate any official documentation guiding such a task, so we suggest looking into the source code of the existing plugins as the first step.
These source codes can be especially helpful:
If you liked this post, please consider upvoting this issue on GitHub. Hopefully, that way this aggregate
transform shall get natively supported by Apache ECharts and we developers won't need to rely on any third-party solution. 🕺
Top comments (0)