11import type React from 'react' ;
2- import { useState } from 'react' ;
2+ import { useRef , useState } from 'react' ;
33import { createPortal } from 'react-dom' ;
44
55export type UseCustomElementPortalParams = {
@@ -8,7 +8,7 @@ export type UseCustomElementPortalParams = {
88} ;
99
1010export type UseCustomElementPortalReturn = {
11- portal : ( ) => React . JSX . Element ;
11+ portal : React . ComponentType ;
1212 mount : ( node : Element ) => void ;
1313 unmount : ( ) => void ;
1414 id : number ;
@@ -18,19 +18,47 @@ export type UseCustomElementPortalReturn = {
1818// the given component into a given node
1919export const useCustomElementPortal = ( elements : UseCustomElementPortalParams [ ] ) => {
2020 const [ nodeMap , setNodeMap ] = useState < Map < string , Element | null > > ( new Map ( ) ) ;
21+ const nodeMapRef = useRef ( nodeMap ) ;
22+ const elementsRef = useRef < Map < string , React . ReactNode > > ( new Map ( ) ) ;
23+ const portalsRef = useRef < Map < string , UseCustomElementPortalReturn > > ( new Map ( ) ) ;
2124
22- return elements . map ( el => ( {
23- id : el . id ,
24- mount : ( node : Element ) => setNodeMap ( prev => new Map ( prev ) . set ( String ( el . id ) , node ) ) ,
25- unmount : ( ) =>
26- setNodeMap ( prev => {
27- const newMap = new Map ( prev ) ;
28- newMap . set ( String ( el . id ) , null ) ;
29- return newMap ;
30- } ) ,
31- portal : ( ) => {
32- const node = nodeMap . get ( String ( el . id ) ) ;
33- return node ? createPortal ( el . component , node ) : null ;
34- } ,
35- } ) ) ;
25+ nodeMapRef . current = nodeMap ;
26+ elementsRef . current = new Map ( elements . map ( el => [ String ( el . id ) , el . component ] ) ) ;
27+
28+ const elementIds = new Set ( elements . map ( el => String ( el . id ) ) ) ;
29+ portalsRef . current . forEach ( ( _ , id ) => {
30+ if ( ! elementIds . has ( id ) ) {
31+ portalsRef . current . delete ( id ) ;
32+ }
33+ } ) ;
34+
35+ return elements . map ( el => {
36+ const id = String ( el . id ) ;
37+ const existingPortal = portalsRef . current . get ( id ) ;
38+
39+ if ( existingPortal ) {
40+ return existingPortal ;
41+ }
42+
43+ const portal : React . ComponentType = ( ) => {
44+ const node = nodeMapRef . current . get ( id ) ;
45+ const component = elementsRef . current . get ( id ) ;
46+ return node ? createPortal ( component , node ) : null ;
47+ } ;
48+
49+ const customElementPortal = {
50+ id : el . id ,
51+ mount : ( node : Element ) => setNodeMap ( prev => new Map ( prev ) . set ( id , node ) ) ,
52+ unmount : ( ) =>
53+ setNodeMap ( prev => {
54+ const newMap = new Map ( prev ) ;
55+ newMap . set ( id , null ) ;
56+ return newMap ;
57+ } ) ,
58+ portal,
59+ } ;
60+
61+ portalsRef . current . set ( id , customElementPortal ) ;
62+ return customElementPortal ;
63+ } ) ;
3664} ;
0 commit comments