【React】Chart.jsで凡例を一括で表示・非表示を切り替える

## はじめに

Chart.jsでグラフ作成すると凡例のラベルをクリックすると表示・非表示が切り替えることができるのですが、一括で表示・非表示を切り替えたいことがあったので実装してみました。

## ソース

以下のCodeSandBoxにありますのでお試しください。

https://codesandbox.io/s/react-chartjs-toggle-hidden-vqo03?file=/src/App.js

## 使用したもの

chart.jsのバージョンは以下の通りです。

```json
"chart.js": "2.9.3",
"react-chartjs-2": "2.9.0",
```

## データ

データはなんでもいいですが、積み上げ棒グラフ用のデータを用意します。

```js:dataset.js
export const data = {
  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"
    }
  ]
};
```

## グラフオプション

積み上げ棒グラフなので`stackedをtrue`にします。
その他は適当に設定します。

```js:dataset.js
export const 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
};
```

## グラフを表示

とりあえずグラフだけ表示します。
`react-chartjs-2`から`Bar`コンポーネントをインポートして、`data`と`options`を渡してあげれば表示できます。

```js:App.js
import React from "react";
import { Bar } from "react-chartjs-2";
import { data, options } from "./dataset";

const App = () => {
  return (
    <div>
      <Bar data={data} options={options} height={400} />
    </div>
  );
};
export default App;
```

## グラフの凡例一括表示・非表示ボタンを作る

### 表示・非表示のステート作る

表示・非表示のステートを管理するためのステートを用意します。

```js:App.js
import { useState } from "react"

const [isDisplay, setIsDisplay] = useState(true);
```

### グラフアクセス用のrefを用意する

グラフアクセス用に`ref`を用意します。

```js:App.js
import { useRef } from "react"

const ref = useRef(null);
```

### 表示・非表示を切り替えるボタンを作る

切り替えボタンを用意します。
ステートを参照してボタンのテキストを切り替えています。
ボタンクリック時の`onClickToggle`関数はこの後つくります。

```js:App.js
  <button onClick={onClickToggle}>
    {isDisplay ? "全て非表示" : "全て表示"}
  </button>
```

### ボタンクリック時の処理を作る

ref.currentでChart.jsインスタンスの中にアクセスすると、`hidden`で表示・非表示を切り替えていることがわかるので、すべての要素にアクセスして一括でTrue/Falseを切り替えてあげます。
最後に`update()`を呼んであげれば表示を切り替えることができます。

ついでに`setIsDisplay`でステートも切り替えておきます。

```js:App.js
  const onClickToggle = () => {
    ref.current.chartInstance.data.datasets.forEach((dataset) => {
      Object.keys(dataset._meta).forEach((key) => {
        dataset._meta[key].hidden = isDisplay;
      });
    });

    setIsDisplay(!isDisplay);
    ref.current.chartInstance.update();
  };
```

### `Bar`コンポーネントにrefを渡す

Chart.jsのインスタンスには、次のように要素へのrefを配置することでアクセスできます。

```js:App.js
<Bar data={data} options={options} height={400} ref={ref} />
```

### 全文

最終的に以下のようになります。

```diff:App.js
- import React from "react";
+ import React, { useRef, useState } from "react";
import { Bar } from "react-chartjs-2";
import { data, options } from "./dataset";

const App = () => {
+  const [isDisplay, setIsDisplay] = useState(true);
+  const ref = useRef(null);
+  const onClickToggle = () => {
+    ref.current.chartInstance.data.datasets.forEach((dataset) => {
+      Object.keys(dataset._meta).forEach((key) => {
+        dataset._meta[key].hidden = isDisplay;
+      });
+    });
+    setIsDisplay(!isDisplay);
+    ref.current.chartInstance.update();
+  };

  return (
+    <>
+      <button onClick={onClickToggle}>
+        {isDisplay ? "全て非表示" : "全て表示"}
+      </button>
      <div>
        <Bar data={data} options={options} height={400} ref={ref} />
      </div>
+    </>
  );
};
export default App;

```

## まとめ

これでChart.jsグラフの凡例ラベルの一括表示・非表示切り替えボタンの実装ができました。
誰かの参考になればいいなと思います。

間違っているとかこうしたほうがいいよとかありましたら指摘お願いします。

## 参考

https://stackoverflow.com/questions/49249902/select-all-and-unselect-option-for-chart-js/49252102#49252102

https://github.com/reactchartjs/react-chartjs-2