ReactでApexChartsを動かす

githubのトレンドを眺めていたら良さげなチャートライブラリApexChartsを見つけたのでReactで使ってみた。
今回は公式のCreating Your First JavaScript Chartのサンプルを参考にする。

ApexChartsのインストール

npm install apexcharts --save

timestampのフォーマットにMoment.jsを使うのでインストールする

npm install moment --save

FWを使わない場合インスタンスを作成してrender関数を実行するだけで描画できるがReactのrender内でApexChartsのrenderを使うわけにもいかない。

そのためcomponentDidMountでApexChartsのインスタンス化とrenderの処理する。

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import ApexCharts from 'apexcharts'
import moment from 'moment'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      options: {
        chart: {
          type: 'line'
        },
        series: [
          {
            name: 'sales',
            data: [30, 40, 35, 50, 49, 60, 70, 91, 125]
          }
        ],
        xaxis: {
          categories: [
            1533049200,
            1533135600,
            1533222000,
            1533308400,
            1533394800,
            1533481200,
            1533567600,
            1533654000,
            1533740400,
          ],
          labels: {
            formatter: value => {
              return moment.unix(value).format('YYYY-MM-DD')
            }
          }
        }
      },
      nextData: []
    }
  }

  componentDidMount() {
    this.chart = new ApexCharts(
      this.node,
      this.state.options,
    )
    this.chart.render()
  }

  render() {
    return (
      <div className="App">
        <h1>Sample apexcharts.js</h1>
        <div id="chart" ref={node => (this.node = node)} />
      </div>
    )
  }
}

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)

一度表示してからデータセットに変更がないならこれで問題ないが、何らかのアクションでデータセットに変更があった場合に再描画できない。

そこでcomponentDidUpdate内でApexChartsのインスタンスが保持しているデータセットを更新して再描画する。
アップデートに使えるメソッドは以下の通り

  • updateOptions (newOptions, redrawPaths, animate)
  • updateSeries (newSeries, animate)

どちらも第一引数で受け取ったプロパティで既存のデータセットをオーバーライドする。

updateOptionsの第二引数のredrawPathsは現在のパスを起点にアニメーションを開始するか最初からパスを描画するかを指定できる。 デフォルトではfalseでtrueを指定で最初から描画する。
共通のanimateはパスの描画をアニメーションさせるかを指定できる。

appendDataとかでもrenderが走るけどこっちはオーバーライドではなくSeriesに新しいデータセットを追加するので今回は省略。

サンプルではボタンをクリックしてseriesだけを更新するのでupdateSeriesを使う。

handleClick() {
    // 長さ9の配列を作成する
    // 各valueは最大130でランダムに作成する
    const newData = [...Array(9).keys()].map(() => {
      return Math.floor(Math.random() * 130)
    })
    this.setState({ nextData: newData })
}

componentDidUpdate() {
  const { nextData } = this.state
  this.chart.updateSeries([
    {
      name: 'sales',
      data: nextData,
    },
  ])
}

// ボタンを追加
render() {
  return (
    <div className="App">
      <h1>Sample apexcharts.js</h1>
      <div id="chart" ref={node => (this.node = node)} />
      <button onClick={this.handleClick}>change state!!</button>
    </div>
  )
}

constructorに以下の処理も追加する

this.handleClick = this.handleClick.bind(this)

xaxis.labals.formatterに定義したカスタム関数でcategoriesの各valueをフォーマットできる。
今回はtimestampをmomentjsでYYYY-MM-DDの形式にフォーマットしてる。

labels: {
  formatter: value => {
    return moment.unix(value).format('YYYY-MM-DD')
  }
}

componentWillUnmountの中でchartのインスタンスを破棄してあげたほうがいいかもしれない。

componentWillUnmount() {
  this.chart.destroy()
}

まとめ

ざっくり使ってみたけどApexChartsが提供するAPI自体は少ない。(現在11個)
グラフの種類やデザイン含め大抵はOptionsのパラメータを変えるだけで済むので 描画処理の部分は使いまわしがききそう。
学習コストも低いので管理画面とかにサクッと導入して見るのもあり。

codesandboxが便利すぎる。

今回作成したサンプル