Home » Web » React » Simple Pattern to use Highchart and React

Simple Pattern to use Highchart and React

At the time I am compositing this entry, Highchart doesn’t collaborate naturally in a React project. There is an open source project available but it is not part of the official release. However, using directly the library from Highchart into your TypeScript and React project is not a huge burden. In this article, we will see how to create a React component that uses Highchart.

The first step is to get the official library and the definition file. The types are not in the main repository and are a major version late. However, it was enough in my experience to work. You need to NPM install “highcharts” and “@types/highcharts”.

You will need to create a new TSX file and import one or many packages. The number of packages depends on what you want to do with HighChart. If you need to use the exporting capability, you will need to import an additional module. If you need the offline flavor of exporting, you will need also to import a package. For the completeness of the example, let’s get them all.

import Highcharts, { CSSObject, Options, SeriesObject, PlotBands } from "highcharts";
import Exporter from "highcharts/modules/exporting";
import OE from "highcharts/modules/offline-exporting";

The Exporting and OE are functions. They must be applied to the HightCharts function right below you imports.

Exporter(Highcharts);
OE(Highcharts);

The next step is to create a React component that will have a componentDidUpdate, a render, and a componentDidMount. Also, the component must have one reference to Highchart and one other reference to the HTML container. The container may be used for adding dynamic animation with CSS and also to indicate where to inject the Highchart component. The Highchart component reference will be used to push data or user’s options during the lifetime of the component.

private hightChartContainer: HTMLDivElement | null = null;
private highChartControl: Highcharts.ChartObject | undefined = undefined;

The render function creates the container that hosts the Highchart control.

    public render(): JSX.Element {
        const isChartVisible = this.props.source.graphData !== undefined;
        const chart = <div
            style={{
                display: (isChartVisible ? "block" : "none")
            }}
            ref={(div) => { this.hightChartContainer = div; }}
        />;
        return <>
            <div
                style={{
                    display: (isChartVisible ? "none" : "flex"),
                }}
            >
                Loading your data
            </div>
            {chart}
        </>;
    }

The render will be executed once and then the componentDidMount function is executed once as well. Meanwhile, the componentDidMount function does the heavy lifting by instantiating the Highchart component. In reality, this function is way bigger than what I am providing. It contains all the configuration you can apply to a Highchart.

public componentDidMount() {
        const that = this;
        if (this.hightChartContainer !== null) {
            Highcharts.setOptions(theme);
            const options: Options = {
                chart: {
                    type: "line",
                },
                series: [],
            };
            this.highChartControl = Highcharts.chart(this.hightChartContainer, options);
            if (this.props.source.graphData !== undefined) {
                this.componentDidUpdate();
            }
        }
    }

Finally, the componentDidUpdate has the role to push the values to the chart. The componentDidMount sets the series to an empty array because the data might not yet be available. However, the componentDidUpdate will be called every time new properties change which make it a good place to update the chart.

public componentDidUpdate() {
        if (this.hightChartContainer !== null && this.highChartControl !== undefined && this.props.source.graphData !== undefined) {
            const data = this.props... // Get your data
            const series: GraphData[] = [];
            while (this.highChartControl.series.length > 0) {
                this.highChartControl.series[0].remove(false);
            }

            // Values dimension represent each series
            scenarioUpdate.addMarker("CreateSeries");
            rawValues.forEach((value: number[], index: number) => {
                const time = data.start + index * data.step;
                for (let iSerie = 0; iSerie < value.length; iSerie++) {
                    if (index === 0) { // Only first time we create series
                        series.push({
                            seriesName: data.legend[iSerie],
                            seriesValues: []
                        });
                    }
                    series[iSerie].seriesValues.push({ time: time, value: value[iSerie] });
                }
            });

            let loopCount = 0;
            series.forEach((serieData: GraphData, index: number) => {
                if (this.highChartControl !== undefined) {
                    const values = serieData.seriesValues.map((d: GraphDataValue) => { loopCount++; return [d.time, d.value]; });
                    this.highChartControl.addSeries({}, false, false);
                    this.highChartControl.series[index].setData(values, false, false, false);
                    this.highChartControl.series[index].update({ name: serieData.seriesName }, false);
                }
            });

            window.requestAnimationFrame(() => {
                window.requestAnimationFrame(() => {
                    if (this.highChartControl !== undefined) {
                        this.highChartControl.reflow(); 
                        this.highChartControl.redraw(); 
                    }
                });
            });
        }
    }

Once again, I removed a lot of the code from which I have been inspired. The goal here is to create all your series but before creating series, we remove all the existing one. In the end, we reflow and redraw which animated the graph. Indeed, a good “shouldComponentUpdate” is required to avoid updating for no reason.

It might look like a lot of code but in reality, it is so close to the Highcharts documentation that it is easy to maintain. The key concept is to have the container that host Highchart build one time and to update the existing one.

If you like my article, think to buy my annual book, professionally edited by a proofreader. directly from me or on Amazon. I also wrote a TypeScript book called Holistic TypeScript

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.