blob: 5906b30f529112422cc8960c9d27092ead379b56 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
import React, { useLayoutEffect, useRef } from "react";
import { useTunnels } from "../../context/tunnels";
import { atom } from "../../editor-jotai";
export const withInternalFallback = <P,>(
componentName: string,
Component: React.FC<P>,
) => {
const renderAtom = atom(0);
const WrapperComponent: React.FC<
P & {
__fallback?: boolean;
}
> = (props) => {
const {
tunnelsJotai: { useAtom },
} = useTunnels();
// for rerenders
const [, setCounter] = useAtom(renderAtom);
// 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 <Component {...props} />;
};
WrapperComponent.displayName = componentName;
return WrapperComponent;
};
|