amCharts is a JavaScript library for data visualization with built-in support for TypeScript and ES6 modules. It is also fully compatible with Angular, React, Vue.js. amCharts offers a wide selection of types of charts, as presented here.
I've been using amCharts for months and now I know its major strengths and downsides. When used for static charts, it's simply amazing. What about dynamic charts?
Initializing an amCharts chart takes a lot of time and resources. Imagine doing this every time we need to change the data visualized, i.e. as a result of an event. This can easily become the bottleneck of a web application, especially if there are many charts (depending on this event) on the same page. This is where memoization comes into play!
Please note that the main goal of this article is not to show how either how amCharts works, or what memoization is. This is why I strongly recommend reading this and this first.
Let's build an efficient React component designed to wrap any type of amCharts chart.
Building The Chart Component
Our generic component can be defined as follows:
function Chart(props) {
const { idHtmlElement, onInitialize, ...remainingProps } = props;
const [chart, setChart] = useState(undefined);
const initializeChart = React.useMemo(() => {
return () => {
const chart = am4core.create(idHtmlElement, am4charts.XYChart);
// ... customizing the chart
return chart;
}
}, []
)
useEffect(() => {
const chart = initializeChart();
// saving the chart reference in the component's state
// since it might be useful
setChart(chart);
// passing the chart reference to the parent component
onInitialize(chart);
}, []);
return (
<div
id={idHtmlElement}
{...remainingProps}
/>
)
}
export default Chart;
The am4core.create
function returns the chart instance, which is what allows us to manipulate the behavior of the chart itself. The creation of the chart, i.e. its initialization, should be executed only the first time the component is rendered. Otherwise, this component would become extremely inefficient. This is why we wrapped our initialization function in a useMemo callback. This way, we have memoized the function returning the char instance, which is called only when the component is invoked for the first time. Every time the component re-renders the chart
cached value will be used, avoiding the initialization overhead.
If we want our chart to be manipulated without being created anew every time, we need to expose its reference to the parent component. For this reason, we added the onInitialization
prop. Thanks to it, the parent can change the appearance, data, and behavior of the chart operating directly on its instance.
The main strengths of this approach are two:
Avoiding code duplication by creating a component per chart type
Allowing the parent component to interact directly with the chart whatever and whenever it wants
ChartComponent
In Action
Let's say we want to create a temporal evolution line chart, just like this one:
The data to be visualized will be retrieved from an AJAX call, whose result depends on the time span selected by the user. First of all, let's define the TemporalEvolutionChart
component:
function TemporalEvolutionChart(props) {
const { idHtmlElement, onInitialize, ...remainingProps } = props;
const [chart, setChart] = useState(undefined);
const initializeChart = React.useMemo(() => {
return () => {
const chart = am4core.create(idHtmlElement, am4charts.XYChart);
// defining the temporal evolution line chart
const categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "time";
const valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
const lineSeries = chart.series.push(new am4charts.LineSeries());
lineSeries.dataFields.valueY = "value";
lineSeries.dataFields.categoryX = "time";
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.parent = chart.bottomAxesContainer;
chart.legend = new am4charts.Legend();
return chart;
}
}, []
)
useEffect(() => {
const chart = initializeChart();
// saving the chart reference in the component's state
// since it might be useful
setChart(chart);
// passing the chart reference to the parent component
onInitialize(chart);
}, []);
return (
<div
id={idHtmlElement}
{...remainingProps}
/>
)
}
export default TemporalEvolutionChart;
The parent component is in charge of making the call to retrieve the data to be shown in the chart and pass it to it. This is done by assigning the result of the AJAX call to the data
property of the chart instance. amCharts chart will automatically render the new data.
`
Et voilà! As soon as a user changes the time span of interest, an AJAX call is made (thanks to the API definition layer introduced here), and the chart is updated accordingly. This, in a very efficient way without initializing the chart every time.
Conclusion
In this article, we showed an efficient way to use amCharts with React. Initializing an amCharts chart is a time-consuming operation, which should be executed only when absolutely necessary. This approach is a great example of how to save resources and prevent users from feeling frustrated due to a very slow web page. Thanks for reading! I hope that you found this article helpful.
`
The post "Efficient Data Visualization with React" appeared first on Writech.
Top comments (0)