Using Apache ECharts with React and TypeScript: Using Aggregate Transform
Tags: typescript, react, echarts, webdev
Published: 2022-02-27 11:50:00

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](https://apache.github.io/echarts-handbook/en/concepts/data-transform/) **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](https://github.com/100pah/echarts-simple-transform) and [echarts-stat](https://www.npmjs.com/package/echarts-stat). ✨

[echarts-simple-transform](https://github.com/100pah/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](https://www.npmjs.com/package/@manufac/echarts-simple-transform).

This article shall delve into how to use [@manufac/echarts-simple-transform](https://www.npmjs.com/package/@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: 

1. [Using Apache ECharts with React and TypeScript](https://dev.to/maneetgoyal/using-apache-echarts-with-react-and-typescript-353k) 
2. [Using Apache ECharts with React and TypeScript: Optimizing Bundle Size](https://dev.to/maneetgoyal/using-apache-echarts-with-react-and-typescript-optimizing-bundle-size-29l8)

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](https://apache.github.io/echarts-handbook/en/concepts/data-transform/#use-external-transforms) 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.

```ts
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 in `as ExternalDataTransform` is needed due to a type mismatch error. Since `@manufac/echarts-simple-transform` exports `ExternalDataTransform`, the type mismatch error is easily dealt with using `as` assertion. This isn't possible with `echarts-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 inside `echarts-simple-transform` such that `as` assertion isn't needed. After trying out various alternates, we speculate that some changes are needed in the `echarts` project itself to update the `registerTransform` function typings so that it can easily accommodate external/third-party transforms.


- `echarts-simple-transform` has an [issue](https://github.com/100pah/echarts-simple-transform/issues/1) 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](https://github.com/manufac-analytics/echarts-simple-transform#differences-vis-%C3%A0-vis-the-og-100pahecharts-simple-transform). 

---

### How to use the `aggregate` transform?

Here's the complete recipe:

```ts
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](https://github.com/manufac-analytics/echarts-simple-transform#aggregate-transform). 

---

### 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: 

- [Aggregate Transform](https://github.com/100pah/echarts-simple-transform/blob/main/src/aggregate.ts)
- [Regression Transform](https://github.com/ecomfe/echarts-stat/blob/master/src/transform/regression.js)

---

If you liked this post, please consider upvoting this [issue](https://github.com/apache/echarts/issues/15306) 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. 🕺