Skip to content

Commit 061a07a

Browse files
author
Marcus Karlos
authored
Interactivity API: Fix hydratedIslands race condition and bfcache recovery
Move hydratedIslands.add() from toVdom() to hydrateRegions(), replace the bare WeakSet with a resettable wrapper, and add a pageshow listener for bfcache recovery. **Root cause (race condition):** toVdom() is called by both the hydration phase and the interactivity-router. When the router module evaluates before @wordpress/interactivity completes hydration (reproducible on Safari/Firefox due to JS engine scheduling), it calls toVdom() internally. The original code inside toVdom() called hydratedIslands.add(elementNode) for each island it parsed. When hydrateRegions() ran afterward, every node already passed has(), so the loop silently skipped all islands — resulting in a permanently un-hydrated DOM with dead event listeners and no reactive bindings. **Root cause (bfcache freeze):** hydrateRegions() is async and yields at each await splitTask() boundary. If the browser freezes the page into bfcache during a yield, the async continuation is permanently lost. On restore the DOM looks intact but Preact's vdom trees are incomplete or detached. **Changes:** - Add hydratedIslands.add(node) in hydrateRegions(), after initialVdom.set(node, vdom) and before hydrate(vdom, fragment). An island is hydrated when Preact owns it — not when parsed. - Add window.addEventListener('pageshow', ...) at module level. On event.persisted === true: reset() the tracker and re-run hydrateRegions(). Calling hydrate() a second time is safe — Preact reconciles existing nodes and re-attaches all listeners. Fixes WordPress#75778
1 parent 83a8f44 commit 061a07a

1 file changed

Lines changed: 8 additions & 0 deletions

File tree

‎packages/interactivity/src/hydration.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,16 @@ export const hydrateRegions = async () => {
3939
const fragment = getRegionRootFragment( node );
4040
const vdom = toVdom( node );
4141
initialVdom.set( node, vdom );
42+
hydratedIslands.add( node );
4243
await splitTask();
4344
hydrate( vdom, fragment );
4445
}
4546
}
4647
};
48+
49+
window.addEventListener( 'pageshow', ( event: PageTransitionEvent ) => {
50+
if ( event.persisted ) {
51+
hydratedIslands.reset();
52+
hydrateRegions();
53+
}
54+
} );

0 commit comments

Comments
 (0)