import { atom, useAtom } from "jotai"; import React, { useLayoutEffect, useRef } from "react"; import { useTunnels } from "../../context/tunnels"; export const withInternalFallback =
( componentName: string, Component: React.FC
,
) => {
const renderAtom = atom(0);
const WrapperComponent: React.FC<
P & {
__fallback?: boolean;
}
> = (props) => {
const { jotaiScope } = useTunnels();
// for rerenders
const [, setCounter] = useAtom(renderAtom, jotaiScope);
// for initial & subsequent renders. Tracked as component state
// due to excalidraw multi-instance scanerios.
const metaRef = useRef({
// flag set on initial render to tell the fallback component to skip the
// render until mount counter are initialized. This is because the counter
// is initialized in an effect, and thus we could end rendering both
// components at the same time until counter is initialized.
preferHost: false,
counter: 0,
});
useLayoutEffect(() => {
const meta = metaRef.current;
setCounter((c) => {
const next = c + 1;
meta.counter = next;
return next;
});
return () => {
setCounter((c) => {
const next = c - 1;
meta.counter = next;
if (!next) {
meta.preferHost = false;
}
return next;
});
};
}, [setCounter]);
if (!props.__fallback) {
metaRef.current.preferHost = true;
}
// ensure we don't render fallback and host components at the same time
if (
// either before the counters are initialized
(!metaRef.current.counter &&
props.__fallback &&
metaRef.current.preferHost) ||
// or after the counters are initialized, and both are rendered
// (this is the default when host renders as well)
(metaRef.current.counter > 1 && props.__fallback)
) {
return null;
}
return