Skip to content

Commit

Permalink
fix(FloatingPortal): unconditional rendering with Suspense (#2764)
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks committed Jan 22, 2024
1 parent 767b023 commit 196f215
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 40 deletions.
5 changes: 5 additions & 0 deletions .changeset/stupid-scissors-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@floating-ui/react": patch
---

fix(FloatingPortal): unconditional rendering with Suspense
80 changes: 40 additions & 40 deletions packages/react/src/components/FloatingPortal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const PortalContext = React.createContext<null | {
afterOutsideRef: React.RefObject<HTMLSpanElement>;
}>(null);

const attr = createAttribute('portal');

export function useFloatingPortalNode({
id,
root,
Expand All @@ -44,60 +46,58 @@ export function useFloatingPortalNode({

const uniqueId = useId();
const portalContext = usePortalContext();
const portalNodeRef = React.useRef<HTMLDivElement | null>(null);

const data = React.useMemo(
() => ({id, root, portalContext, uniqueId}),
[id, root, portalContext, uniqueId],
);

const dataRef = React.useRef<typeof data>();

// biome-ignore lint/correctness/useExhaustiveDependencies: `data` is intentionally specified
useModernLayoutEffect(() => {
return () => {
portalNode?.remove();
// Allow the subsequent layout effects to create a new node on updates.
// The portal node will still be cleaned up on unmount.
// https://github.com/floating-ui/floating-ui/issues/2454
queueMicrotask(() => {
portalNodeRef.current = null;
});
};
}, [portalNode, data]);
}, [portalNode]);

useModernLayoutEffect(() => {
if (dataRef.current === data) return;
if (portalNodeRef.current) return;
const existingIdRoot = id ? document.getElementById(id) : null;
if (!existingIdRoot) return;

dataRef.current = data;
const subRoot = document.createElement('div');
subRoot.id = uniqueId;
subRoot.setAttribute(attr, '');
existingIdRoot.appendChild(subRoot);
portalNodeRef.current = subRoot;
setPortalNode(subRoot);
}, [id, uniqueId]);

const {id, root, portalContext, uniqueId} = data;
useModernLayoutEffect(() => {
if (portalNodeRef.current) return;

const existingIdRoot = id ? document.getElementById(id) : null;
const attr = createAttribute('portal');

if (existingIdRoot) {
const subRoot = document.createElement('div');
subRoot.id = uniqueId;
subRoot.setAttribute(attr, '');
existingIdRoot.appendChild(subRoot);
setPortalNode(subRoot);
} else {
let container = root || portalContext?.portalNode;
if (container && !isElement(container)) container = container.current;
container = container || document.body;

let idWrapper: HTMLDivElement | null = null;
if (id) {
idWrapper = document.createElement('div');
idWrapper.id = id;
container.appendChild(idWrapper);
}
let container = root || portalContext?.portalNode;
if (container && !isElement(container)) container = container.current;
container = container || document.body;

const subRoot = document.createElement('div');
let idWrapper: HTMLDivElement | null = null;
if (id) {
idWrapper = document.createElement('div');
idWrapper.id = id;
container.appendChild(idWrapper);
}

subRoot.id = uniqueId;
subRoot.setAttribute(attr, '');
const subRoot = document.createElement('div');

container = idWrapper || container;
container.appendChild(subRoot);
subRoot.id = uniqueId;
subRoot.setAttribute(attr, '');

setPortalNode(subRoot);
}
}, [data]);
container = idWrapper || container;
container.appendChild(subRoot);

portalNodeRef.current = subRoot;
setPortalNode(subRoot);
}, [id, root, uniqueId, portalContext]);

return portalNode;
}
Expand Down

0 comments on commit 196f215

Please sign in to comment.