6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Reactでchartjsを使ったときにY軸のMAX値の設定ではまった話

Last updated at Posted at 2020-05-28

はじめに

Reactでchart.jsを使って積み上げ棒グラフ表示したときに、Y軸のMAX値の設定ではまったことがあったので、まとめてみます。

使ったもの

  • react(v16.13.1)
  • react-chartjs-2(v2.9.0)
  • chart.js(v2.9.3)
  • lodash(v4.17.15)

作りたかったもの

  • 積み上げ棒グラフ
  • ボタンクリックで実数表示⇔割合表示を切り替える
  • 実数表示のY軸の最大値はauto(kg)にしたい(単位は適当)
  • 割合表示のY軸の最大値は100(%)にしたい

イメージはボタンクリックでstateで管理してるフラグを切り替えて、chart.jsのコンポーネントに渡すdataoptionsを変化させるイメージです。

プロジェクト作成

create-react-appでReactのプロジェクトを作成して、上記のとおり必要なパッケージをインストールします。(react以外)

create-react-app app
npm install --save react-chartjs-2 chart.js lodash

データの準備

とりあえず適当に準備する。

App.js
    this.dataset = {
      labels: ["A", "B", "C", "D", "E"],
      datasets: [
        {
          label: "label1",
          data: [36.1, 41.2, 50.4, 32.9, 29.4],
          backgroundColor: "red",
        },
        {
          label: "label2",
          data: [51.9, 60.5, 45.6, 55.5, 75.5],
          backgroundColor: "blue",
        },
        {
          label: "label3",
          data: [80.1, 88.2, 75.7, 69.4, 100.2],
          backgroundColor: "green",
        },
        {
          label: "label4",
          data: [120.5, 110.5, 128.9, 130.2, 150.5],
          backgroundColor: "purple",
        },
        {
          label: "label5",
          data: [80.5, 85.9, 77.7, 68.5, 60.9],
          backgroundColor: "yellow",
        },
      ],
    };

オプションの準備

積み上げ棒グラフのオプションを準備する。

とりあえず、実数表示用にscales.yAxes[0].scaleLabel.labelStringに実数(kg)を入れておく。
後でレンダー時に割合表示なら書き換える。

App.js
    this.options = {
      scales: {
        xAxes: [
          {
            stacked: true,
            display: true,
            scaleLabel: {
              display: true,
            },
          },
        ],
        yAxes: [
          {
            stacked: true,
            display: true,
            scaleLabel: {
              display: true,
              labelString: "実数(kg)",
            },
            ticks: {
              min: 0,
            },
          },
        ],
      },
      maintainAspectRatio: false,
    };

レンダー部分

データとオプションをBarコンポーネントに渡せばグラフが描画できる。
データとオプションは実数表示か割合表示かを変更するように関数を呼んで生成

App.js
render() {
    const data = this.changeDataSet();
    const options = this.changeOptions();
    console.log(options);
    return (
      <div>
        <input type="button" value="変更" onClick={this.onClickButton} />
        <Bar data={data} options={options} height={400} />
      </div>
    );
  }

this.changeDataSet部分

this.state.isProbで実数表示か割合表示かを切り替える。
lodashcloneDeepで値だけコピーして、そちらを加工する。

割合表示時は小数点以下第二位を四捨五入する。

App.js
  changeDataSet() {
    let dataset = _.cloneDeep(this.dataset);

    // 割合表示の時にデータを変更
    let sum = 0;
    if (this.state.isProb === true) {
      for (let j = 0; j < dataset.datasets[0].data.length; j++) {
        sum = 0;
        for (let i = 0; i < dataset.datasets.length; i++) {
          sum += dataset.datasets[i].data[j];
        }

        for (let i = 0; i < dataset.datasets.length; i++) {
          if (sum > 0) {
            dataset.datasets[i].data[j] = _.round(
              (dataset.datasets[i].data[j] / sum) * 100, 1 );
          } else {
            dataset.datesets[i].data[j] = 0;
          }
        }
      }
    }
    return dataset;
  }

this.changeOptions部分

同じくthis.state.isProbで実数表示か割合表示かを切り替える。
lodashcloneDeepで値だけコピーして、そちらを加工する。

とりあえずscales.yAxes[0].scaleLabel.labelStringだけ変更する。

App.js
  changeOptions(data) {
    let options = _.cloneDeep(this.options);

    if (this.state.isProb === true) {
      options.scales.yAxes[0].scaleLabel.labelString = "割合(%)";
    }
    return options;
  }

確認してみる

実数表示時(初期表示時)

scales.yAxes[0].ticks.maxを設定していないので、Y軸の最大値は自動で調整される。

2020-05-28_20h46_11.png

割合表示時

scales.yAxes[0].ticks.maxを設定していないので、Y軸の最大値は自動で調整される。

しかし四捨五入しているので、全部加算した時に100を超えることがあるので、Y軸の最大値が100ではなく120となってしまう。

なので、割合表示時だけY軸の最大値を100にしたい。

2020-05-28_20h49_37.png

this.changeOptionsに追記

割合表示の時だけoptions.scales.yAxes[0].ticks.max = 100;を追記して、Y軸の最大値を100にしてみる。

App.js
  changeOptions(data) {
    let options = _.cloneDeep(this.options);

    if (this.state.isProb === true) {
      options.scales.yAxes[0].scaleLabel.labelString = "割合(%)";
      options.scales.yAxes[0].ticks.max = 100;
    }
    return options;
  }

再度確認してみる

割合表示時

うまくいった!!Y軸の最大値が100になっている!!

2020-05-28_20h54_15.png

しかし、もう一度変更ボタン押して、実数表示に戻すと・・・

2020-05-28_20h55_29.png

Y軸の最大値が100のまま戻らなくなった・・

Barコンポーネントに一度渡したオプションが残るっぽいです。

実数表示時にも最大値を入れてみる

実数表示時にもoptions.scales.yAxes[0].ticks.maxを書き換えれば問題とおもったのでやろうとしたのですが、
合計値がどれくらいの大きさになるかわからない場合にすべてをif文とかで書くのも厳しいし、そもそもグラフのY軸のメモリもいい感じに自動で調整されるので、そのあたりが無理と気づく・・・

で結果どう対応したのか・・・

roundで四捨五入しているところをfloorで切り捨ててとりあえず対応した・・・
合計値が100を超えないのでY軸の最大値も自動調整のままでいけた。

微妙ですが、見た目もそんなにおかしくないような気がするのでまあいいでしょう。(ダメかもしれない・・・)

App.js
dataset.datasets[i].data[j] = _.floor(
  (dataset.datasets[i].data[j] / sum) * 100, 1 );

[コメントより追記]

undefinedを設定すれば、最大値の設定が消えるみたいです。
なので、こちらがベストプラクティスだと思います。

App.js
    if (this.state.isProb === true) {
      options.scales.yAxes[0].scaleLabel.labelString = "割合(%)";
      options.scales.yAxes[0].ticks.max = 100;
    } else {
      options.scales.yAxes[0].ticks.max = undefined;
    }

まとめ

Reactでchart.jsを使うときに、state管理でグラフ描画を切り替えるときにはまった話を書きました。

結局、納得できる解決はできていないので、こうしたらいいんじゃない?とかありましたらコメントお願いします!!

解決はできたが、もっとこうしたらいいんじゃない?とかありましたらコメントお願いします!!

6
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?