LoginSignup
3
3

More than 5 years have passed since last update.

react-chartjs-2でクリックした要素を取得する方法について

Posted at

概要

 Reactについて細かい説明は必要ないでしょう。今流行りのWebフレームワークです。
 Chart.jsも有名ですね。JavaScriptで書かれたグラフ描画用ライブラリです。
 react-chartjs-2は、Chart.jsの各種グラフ要素を、ReactのComponentとして書ける便利なライブラリです。

 さて、react-chartjs-2で私は散布図を描画していました。

image.png

 で、Chart.jsは「凡例の表示をクリックした時、デフォルトではそのグラフを非表示にする」挙動をします。
 その際、「どのグラフの凡例をクリックしたか」を調べたいと思いました。
(下の画像では、「Верный+バルジ4」のグラフが非表示になっています)

image.png

 Chart.jsのドキュメントによると、凡例をクリックした際の標準の処理が載っています。その際、クリックしたグラフの情報が引数に乗るので、これを読み取れれば目的が達成されるのではないかと考えました。

 しかし、 そのコード中のthisってどうやって参照するんだよ 問題が目の前に立ちはだかります。Reactでは「Class ComponentやFunctional Component内にJSXを書いて仮想DOMを表現する」ので、thisの意味が「class本体」や「グローバルのthis」のようになります。つまり、「JSX内のタグの参照(目的とするthis)」をどうやって取得するのかが重要なのです。

解決策

 react-chartjs-2において散布図は、<Scatter>という独自タグで表現されています。その属性(Props)の中にはrefというものがあり、これが当該タグの参照となります。参照を保持するには、React Hooks的に考えるとuseRefを使うべきです。

const OutputGraph: React.FC<{params: IGraphParam[]}> = ({params}) => {
    // <Scatter>への参照情報
    const scatterElement = React.useRef<Scatter>(null);

    ()

    return (
        // 参照を利用される当該タグ
        <Scatter width={450} height={450} data={graphData}
        ref={scatterElement} onElementsClick={onClickGraph}
        options={{
            elements: { line: { tension: 0 } },
            scales: {
                xAxes: [{ scaleLabel: { display: true, labelString: '最終攻撃力' }, }],
                yAxes: [{ scaleLabel: { display: true, labelString: '大破率(%)' }, }]
            },
            showLines: true
        }} />
    );
}

 こうしてオブジェクト自体の参照を取ってしまえば、後はそこから辿っていけば、目的とする情報を取得できます。

    const onClickGraph = () => {
        // 参照を取得する
        const scatterObject = scatterElement.current;
        // ここで丁寧にnullチェックしているのは、scatterObjectが↑の段階だと
        // Scatter | null型なので、スマートキャストする必要があることから
        if (scatterObject == null) {
            return;
        }

        // ここで一旦anyにキャストしているのは、scatterObject.chartInstanceは
        // Chart型と判断されているのに、@types/chart.jsの定義によると、
        // なぜかlegendプロパティが生えていなかったから
        // (キャストしないとエディタがエラーを出してコンパイルできない)
        const temp: any = scatterObject.chartInstance;

        // 強引にanyにキャストしたご利益で、legendプロパティ以下を取得できている
        // temp.legend.legendItemsの中身はもっと複雑な型の配列(AoS)だが、
        // 必要な部分だけ読み取ることで記述量を減らしている
        const legendItems: Array<{text: string, hidden: boolean}> = temp.legend.legendItems;

        // 読み取りは終わったので後はデータ加工のみ
        const ignoreNames = legendItems.filter(item => item.hidden).map(item => item.text);

        // useState()から引っ張ってきた上書き用メソッドを利用する
        // (今回の記事とは無関係)
        setGraphData(createGraphData(ignoreNames));
    };

 ちなみに<Scatter>にはonElementsClick属性があり、名前からしてクリックしたグラフの情報を引数から渡してくれる……そんなふうに考えていた時期が私にもありました。実際にはクリックしても[]しか引数のe: anyに渡されないので、「クリックした」ということしか読み取れないというね! どうしてこうなった!!!

備考

package.jsonによると、

  • react: 16.8.2
  • chart.js": 2.7.3
  • react-chartjs-2: 2.7.4
  • @types/chart.js: 2.7.45

といった環境でした。

3
3
0

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
3
3