1+ import { createRoot , type Root } from 'react-dom/client'
2+ import React , { FunctionComponent } from 'react'
3+
4+ // avoid creating multiple roots for the same element
5+ const roots = new WeakMap < HTMLElement , Root > ( )
6+ function getOrCreateRoot ( element : HTMLElement ) : Root {
7+ let root = roots . get ( element )
8+ if ( root == null ) {
9+ root = createRoot ( element , {
10+ identifierPrefix : 'custom-app' ,
11+ onRecoverableError : console . error ,
12+ } )
13+ roots . set ( element , root )
14+ }
15+ return root
16+ }
17+
18+ export function renderInContainer ( Component : FunctionComponent , container : HTMLElement | null | undefined ) {
19+ if ( container != null ) {
20+ const root = getOrCreateRoot ( container )
21+ root . render ( React . createElement ( Component ) )
22+ }
23+ }
24+
25+ export function onDOMContentMutate ( render : ( ) => void ) {
26+ // observe DOM changes to re-render the custom header and footer
27+ let observations = 0
28+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
29+ console . log ( 'DOMContentLoaded' )
30+ render ( )
31+ new MutationObserver ( ( e , o ) => {
32+ render ( )
33+ for ( const item of e ) {
34+ if ( item . target instanceof HTMLElement ) {
35+ const target = item . target
36+ if ( target . id === 'fern-header' || target . id === 'fern-footer' ) {
37+ if ( observations < 3 ) {
38+ // react hydration will trigger a mutation event
39+ observations ++
40+ } else {
41+ o . disconnect ( )
42+ }
43+ break
44+ }
45+ }
46+ }
47+ } ) . observe ( document . body , { childList : true , subtree : true } )
48+ } )
49+ }
0 commit comments