reactjsnext.jsvisualizationtreemapvoronoi

CarrorSearch Treemap with Next.js 15: "visualization already embedded in the element."


I'm building a Next.js 15 app with a CarrotSearch foamtree component.

Following the example here, I get this error.

Uncaught (in promise) FoamTree: visualization already embedded in the element.

I've tried to prevent recreating the treemap if it's already present on the DOM, but to no avail. Any ideas?

//@ts-ignore
const Treemap = ({ data }) => {
    //@ts-ignore
    const element = useRef()
    const [ treemap, setTreemap ] = useState();

    useEffect(() => {
        import("@carrotsearch/foamtree").then(module => {
            setTreemap(new module.FoamTree({
                element: element.current,
                //...
            }));
        });

        return () => {
            if (treemap) {
                //@ts-ignore
                treemap.dispose();
                //@ts-ignore
                setTreemap(null);
            }
        }
    }, []);

    useEffect(() => {
        if (treemap) {
            //@ts-ignore
            treemap.set("dataObject", data);
        }
    }, [ treemap, data ]);

    return (
        //@ts-ignore
        <div ref={element} className="absolute w-full h-full" id="treemap"></div>
    );
};

export default Treemap;


Solution

  • The problem is a result of the incorrect implementation of the dynamic importing in the example. I've updated the example code to properly handle importing and disposal of FoamTree:

    https://github.com/carrotsearch/nextjs-foamtree/blob/master/src/FoamTree.js


    The original implementation had several problems:

    1. treemap.dispose() was never called.

      The reason for this is that the disposal function returned from useEffect() closed over the undefined value of treemap, so it never called treemap.dispose(). The hook did not specify the treemap dependency, so the hook did not re-run to update the treemap to a non-undefined value once it was available. As a result, FoamTree was not getting disposed after the React component unmounting, which lead to the "already embedded" error on the subsequent mounting.

    2. Race condition ignored.

      The original implementation ignored a potential race condition where the component gets unmounted before the dynamic import finishes loading.


    The updated implementation splits the initialization into three hooks:

    1. Dynamic importing of the FoamTree code.
    2. Initialization of FoamTree.
    3. Setting data to visualize.

    The new implementation behaves correctly when unmounting / re-mounting the component. It also should not fail with React strict mode enabled.