diff --git a/packages/element/src/index.js b/packages/element/src/index.js index de3c66eff86cd..8228f7b2ecd8b 100644 --- a/packages/element/src/index.js +++ b/packages/element/src/index.js @@ -1,6 +1,7 @@ export { default as createInterpolateElement } from './create-interpolate-element'; export * from './react'; export * from './react-platform'; +export * from './react-root'; export * from './utils'; export { default as Platform } from './platform'; export { default as renderToString } from './serialize'; diff --git a/packages/element/src/react-platform.js b/packages/element/src/react-platform.js index 7b35ea8d2a2d5..8b7374c4b8901 100644 --- a/packages/element/src/react-platform.js +++ b/packages/element/src/react-platform.js @@ -1,13 +1,7 @@ /** * External dependencies */ -import { - createPortal, - findDOMNode, - render, - hydrate, - unmountComponentAtNode, -} from 'react-dom'; +import { createPortal, findDOMNode } from 'react-dom'; /** * Creates a portal into which a component can be rendered. @@ -26,26 +20,3 @@ export { createPortal }; * @param {import('./react').WPComponent} component Component's instance. */ export { findDOMNode }; - -/** - * Renders a given element into the target DOM node. - * - * @param {import('./react').WPElement} element Element to render. - * @param {HTMLElement} target DOM node into which element should be rendered. - */ -export { render }; - -/** - * Hydrates a given element into the target DOM node. - * - * @param {import('./react').WPElement} element Element to hydrate. - * @param {HTMLElement} target DOM node into which element should be hydrated. - */ -export { hydrate }; - -/** - * Removes any mounted element from the target DOM node. - * - * @param {Element} target DOM node in which element is to be removed - */ -export { unmountComponentAtNode }; diff --git a/packages/element/src/react-root.js b/packages/element/src/react-root.js new file mode 100644 index 0000000000000..8ca5398616137 --- /dev/null +++ b/packages/element/src/react-root.js @@ -0,0 +1,49 @@ +/** + * External dependencies + */ +import { createRoot, hydrateRoot } from 'react-dom/client'; + +const mountedRoots = new WeakMap(); + +/** + * Renders a given element into the target DOM node. + * + * @param {import('./react').WPElement} element Element to render. + * @param {HTMLElement} target DOM node into which element should be rendered. + */ +export function render( element, target ) { + let root = mountedRoots.get( target ); + if ( ! root ) { + root = createRoot( target ); + mountedRoots.set( target, root ); + } + root.render( element ); +} + +/** + * Hydrates a given element into the target DOM node. + * + * @param {import('./react').WPElement} element Element to hydrate. + * @param {HTMLElement} target DOM node into which element should be hydrated. + */ +export function hydrate( element, target ) { + let root = mountedRoots.get( target ); + if ( ! root ) { + root = hydrateRoot( target, element ); + mountedRoots.set( target, root ); + } else { + root.render( element ); + } +} +/** + * Removes any mounted element from the target DOM node. + * + * @param {Element} target DOM node in which element is to be removed + */ +export function unmountComponentAtNode( target ) { + const root = mountedRoots.get( target ); + if ( root ) { + root.unmount(); + mountedRoots.delete( target ); + } +}