关于 React 如何自定义配置 HighCharts tooltip

前言

当我们结合 React 使用 highCharts 库时,存在一些特殊自定义的情况,比如针对 Tooltip 定制化样式。当然 highCharts 也提供了配置自定义 tooltip 的 formatter 方法,可以支持 html 元素。但是并不够灵活,对于配置复杂样式或组件复用情况下,并不友好。因此寻求新的思路,以下便是社区常用的一个方法,在此记录。

实现

Tooltip 组件封装

import {
  Chart,
  TooltipFormatterCallbackFunction,
  TooltipFormatterContextObject,
} from 'highcharts';
import { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';

const generateTooltipId = (chartId: number) =>
  `highcharts-custom-tooltip-${chartId}`;

interface Props {
  chart: Chart | null;
  children(formatterContext: TooltipFormatterContextObject): JSX.Element;
}

export const Tooltip = ({ chart, children }: Props) => {
  const isInit = useRef(false);
  const [context, setContext] = useState<TooltipFormatterContextObject | null>(
    null
  );

  useEffect(() => {
    if (chart) {
      const formatter: TooltipFormatterCallbackFunction = function () {
        // Ensures that tooltip DOM container is rendered before React portal is created.
        if (!isInit.current) {
          isInit.current = true;

          setTimeout(() => {
            chart.tooltip.refresh.apply(chart.tooltip, [
              this.points ? this.points.map(({ point }) => point) : this.point,
            ]);
            chart.tooltip.hide(0);
          });
        }

        setContext(this);

        return `
${generateTooltipId(chart.index)}">
`
; }; chart.update({ tooltip: { formatter, useHTML: true, }, }); } }, [chart]); const node = chart && document.getElementById(generateTooltipId(chart.index)); return node && context ? ReactDOM.createPortal(children(context), node) : null; };

调用方式

import React, { useState, useCallback } from "react";
import Highcharts, { Chart as HighchartsChart } from "highcharts";
import HighchartsReact from "highcharts-react-official";
import { Tooltip } from "./Tooltip";

const options = {
  title: {
    text: "Custom tooltip as React component"
  },
  series: [
    {
      type: "line",
      data: [1, 22424242424, 3, 4, 5, 123123132]
    }
  ],
  tooltip: {
    useHTML: true
  }
};

export const Chart = () => {
  const [chart, setChart] = useState<HighchartsChart | null>(null);
  const callback = useCallback((chart: HighchartsChart) => {
    setChart(chart);
  }, []);

  return (
    <>
      <HighchartsReact
        highcharts={Highcharts}
        options={options}
        callback={callback}
      />

      <Tooltip chart={chart}>
        {(formatterContext) => {
          const { x, y } = formatterContext;
          return (
            <>
              <div>x: {x}</div>
              <div>y: {y}</div>
              <br />
              <button onClick={() => alert(`x: ${x}, y: ${y}`)}>Action</button>
            </>
          );
        }}
      </Tooltip>
    </>
  );
};

formatterContext 里包含以下常用的字段:

{
	points
	point
	series
	x
	y
}

这些字段在自定义 tooltip 组件时可以传参到组件中获取每个 point 的值。
除了 x,y 值外,可以自定义字段,在 series.keys 配置项中,配置如下:

keys: [
  'x',
  'y',
  'custom.A',
  'custom.B',
  'custom.C',
],
data:[[1, 2, 'A1', 'B1', 'C1'], [1, 2, 'A2', 'B2', 'C2']]

其值便可以在 formatterContext.point.options.custom.AformatterContext.point.options.custom.B 等方式获取。

总结

该实现方式最基本的原理是采用 React 的 createPortal 方法,在指定的元素下渲染子元素。可以理解为类似纯 js 的 getElementById('id').html(children(context))

你可能感兴趣的:(javascript,react.js)