Search code examples
next.jsechartsapache-echartsresize-observer

ResizeObserver only works to resize element once


On click of a button, I show a chart. When the chart is first instantiated, a ResizeObserver is set on the parent container of the chart. My application has a collapsible sidebar and when I collapse it, the chart expands to fill up the extra space as expected. However, when I reopen the sidebar, the chart does not shrink back down.

This is the style of the parent container (it is nested within other flex elements so perhaps that is part of the problem?):

<div id={"chartContainer"}
     style={{
         width: "100%",
         height: "100%",
         border: "2px solid yellow",
     }}
/>

This is the style of the chart:

<div ref={chartRef} style={{ width: "100%", height: "80vh" }}></div>

Relevant component code below, but here is a working example on code sandbox. (If you're on a small screen, you'll have to open the codesandbox in full screen to see all the components. Click "Show Chart" to create the chart, click the "X" in the upper left to collapse the sidebar and the icon in the same place to reopen it.)

export const Chart = () => {
  const chartRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    let chart: ECharts | undefined;
    if (chartRef.current !== null) {
      chart = init(chartRef.current, null, { renderer: "svg" });
    }

    const parentContainer: HTMLDivElement = document.querySelector("#chartContainer");
    
    new ResizeObserver(() => {
      chart.resize();
    }).observe(parentContainer);

    return () => {
      chart?.dispose();
    };
  }, []);

  useEffect(() => {
    //Update chart
    if (chartRef.current !== null) {
      const chart = getInstanceByDom(chartRef.current);
      chart?.setOption(option);
    }
  }, [option]);

  return <div ref={chartRef} style={{ width: "100%", height: "80vh" }}></div>;
};

Solution

  • I found the answer on a more generalized stackoverflow post.

    Essentially, flexboxes enforce a minimum size which is the size of the content. It cannot shrink beyond the size of the content. Adding min-width: 0 to the CSS of the flex container containing the parent container of the chart fixed my problem.

    In my example, I am using the Mantine UI Component Library. The flex component I am using that contains the chartContainer element is called <Stack />.

    Copy of relevant code for posterity: TabContent.tsx

    import { useState } from "react";
    import { Stack, Center, Button, Title, Box } from "@mantine/core";
    import { Chart } from "./Chart";
    import styles from "./TabContent.module.css";
    
    export const TabContent = () => {
      const [show, setShow] = useState(false);
      return (
        <>
          <Stack className={`${styles.stack} ${styles.leftStack}`}>
            <Center>
              <Box>
                <Title order={1} size="h3">
                  Visualize Data Menu Area
                </Title>
              </Box>
            </Center>
            <Box styles={{ padding: "10px", position: "relative" }}>
              <Button onClick={setShow}>Show Chart</Button>
            </Box>
          </Stack>
          <Stack className={`${styles.stack} ${styles.rightStack}`}>
            <div
              id={"chartContainer"}
              style={{
                width: "100%",
                height: "100%",
                border: "2px solid yellow",
                // display: "flex",
              }}
            >
              {show && <Chart />}
            </div>
          </Stack>
        </>
      );
    };
    
    

    Edit I made to TabContent.modules.css:

    .rightStack {
      flex: 1;
      min-width: 0;
    }
    

    Updated codesandbox.