ECharts Legend "Complex" React Component

26 views Asked by At

I'm currently working on a chart in ECHARTS where I need to format the legend labels dynamically based on the data from my React application. I only could get it to work trough renderToString, but this is client-side code . The value which gets used in RawIntlProvider is a react Component, which consists of from react intl.

I NEED to use this value which already contains all metadata needed to format the number in the proper way. RenderedToString returns -> <div><div class="">28.8<span class="formattedNumberUnit"> <!-- -->kWh</span></div></div>

      formatter: (seriesName) => {
    const component = renderToString(
      <RawIntlProvider value={intl}>
        {
          data
            .map((row) => row?.[stateParams?.group?.key])
            .find((row) => row?.["sortValue"] == seriesName)?.["value"]
        }
      </RawIntlProvider>
    );
    const parser = new DOMParser();
    const formattedNumberHTML = parser.parseFromString(
      component,
      "text/html"
    );
    const element = formattedNumberHTML.querySelector("*");
    return element ? element.textContent.trim() : "";
  },
  orient: "horizontal",
  bottom: 0,
  left: "center",
},

any ideas on how to improve this ugly hack?

Problems: Echarts Legend only supports strings, Intl hook (needs to not break any hook-rules),....

1

There are 1 answers

0
Ori Drori On

The React documents have a section about removing renderToString from client code.

Example (not tested):

import { createRoot } from 'react-dom/client';
import { flushSync } from 'react-dom';


formatter: (seriesName) => {
  const div = document.createElement('div');
  const root = createRoot(div);

  const element = flushSync(() => {
    root.render(
      <RawIntlProvider value={intl}>
        {data
          .map((row) => row?.[stateParams?.group?.key])
          .find((row) => row?.["sortValue"] == seriesName)?.["value"]
        }
      </RawIntlProvider>
    );
  });

  return element.textContent.trim();
}

However, this looks like an overkill. You only need a number formatted with a unit. You don't really care about the HTML structure. I would probably use Intl.NumberFormat() constructor directly on the value, to get the number formatted with the unit, however kWh is not supported. It's unclear how React Intl formats the unit because it uses Intl.NumberFormat() under the hood. There's probably a library that supports this units, or you might write a specific function.