53 lines
1016 B
TypeScript
53 lines
1016 B
TypeScript
"use client";
|
|
|
|
import { ReactNode, useLayoutEffect, useMemo } from "react";
|
|
|
|
import { createPortal } from "react-dom";
|
|
|
|
interface Props {
|
|
children: ReactNode;
|
|
className?: string;
|
|
containerId?: string;
|
|
}
|
|
|
|
export default function CustomPortal({
|
|
children,
|
|
className,
|
|
containerId,
|
|
}: Props) {
|
|
// ✅ tạo element 1 lần duy nhất
|
|
const element = useMemo(() => {
|
|
if (typeof window === "undefined") return null;
|
|
|
|
const el = document.createElement("div");
|
|
|
|
if (className) {
|
|
el.className = className;
|
|
}
|
|
|
|
return el;
|
|
}, [className]);
|
|
|
|
useLayoutEffect(() => {
|
|
if (!element) return;
|
|
|
|
const parent = containerId
|
|
? document.getElementById(containerId)
|
|
: document.body;
|
|
|
|
if (!parent) return;
|
|
|
|
parent.appendChild(element);
|
|
|
|
return () => {
|
|
parent.removeChild(element);
|
|
};
|
|
}, [element, containerId]);
|
|
|
|
// ✅ không dùng ref.current
|
|
// ✅ không dùng setState
|
|
if (!element) return null;
|
|
|
|
return createPortal(children, element);
|
|
}
|