diff --git a/src/lib/components/Modal/Modal.spec.tsx b/src/lib/components/Modal/Modal.spec.tsx index d2209c44e..7250b3059 100644 --- a/src/lib/components/Modal/Modal.spec.tsx +++ b/src/lib/components/Modal/Modal.spec.tsx @@ -24,6 +24,16 @@ describe('Components / Modal', () => { waitFor(() => expect(input).toHaveFocus()); }); + it('should be removed from DOM and garbage collected', async () => { + const root = document.createElement('div'); + + const { unmount } = render(); + + unmount(); + + await waitFor(() => expect(root.childNodes.length).toBe(0)); + }); + describe('A11y', () => { it('should have `role="dialog"`', async () => { const user = userEvent.setup(); diff --git a/src/lib/components/Modal/Modal.tsx b/src/lib/components/Modal/Modal.tsx index f3d57ae7d..e4c50edf1 100644 --- a/src/lib/components/Modal/Modal.tsx +++ b/src/lib/components/Modal/Modal.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import { ComponentProps, FC, PropsWithChildren, useRef } from 'react'; +import { ComponentProps, FC, PropsWithChildren, useEffect, useRef } from 'react'; import { createPortal } from 'react-dom'; import type { FlowbiteBoolean, FlowbitePositions, FlowbiteSizes } from '../Flowbite/FlowbiteTheme'; import { useTheme } from '../Flowbite/ThemeContext'; @@ -80,6 +80,19 @@ const ModalComponent: FC = ({ root.appendChild(containerRef.current); } + useEffect(() => { + return () => { + const container = containerRef.current; + + // If a container exists on unmount, it is removed from the DOM and + // garbage collected. + if (container) { + container.parentNode?.removeChild(container); + containerRef.current = null; + } + }; + }, []); + return createPortal(