From 621c73256f072e0e876b534a6d32984b0037c620 Mon Sep 17 00:00:00 2001 From: goldenapples Date: Fri, 31 Jul 2026 15:57:33 -0400 Subject: [PATCH] Wrap navigation events in withSyncEvent HOC to allow preventDefault Since WP 6.8, calling event.preventDefault inside of an Interactivity API router is deprecated and unpredictable. In order to use a link inside an interactive router and prevent its default behavior, we have to wrap the handler in the wp.interactivity.withSyncEvent HOC. --- src/taxonomy/view.js | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/taxonomy/view.js b/src/taxonomy/view.js index 4cbbc5c..fb1f209 100644 --- a/src/taxonomy/view.js +++ b/src/taxonomy/view.js @@ -1,4 +1,21 @@ -import { store, getContext, getElement } from '@wordpress/interactivity'; +import { + store, + getContext, + getElement, + withSyncEvent, +} from '@wordpress/interactivity'; + +/** + * How long the router may spend fetching a navigation before giving up, in + * milliseconds. + * + * The router's default is 10s, after which it falls back to a full-page + * `window.location.assign()`. Slow uncached renders (WP_DEBUG local + * environments, cold caches) regularly brush against that default, turning + * in-place filter updates into full reloads. The extended window keeps slow + * responses on the in-place path; genuinely dead requests still fall back. + */ +const NAVIGATION_TIMEOUT = 30000; const updateURL = async ( action, value, name ) => { const url = new URL( action ); @@ -8,23 +25,25 @@ const updateURL = async ( action, value, name ) => { url.searchParams.delete( name ); } const { actions } = await import( '@wordpress/interactivity-router' ); - await actions.navigate( url.toString() ); + await actions.navigate( url.toString(), { timeout: NAVIGATION_TIMEOUT } ); }; store( 'query-filter', { actions: { - *navigate( e ) { + navigate: withSyncEvent( function* ( e ) { e.preventDefault(); const { actions } = yield import( '@wordpress/interactivity-router' ); - yield actions.navigate( e.target.value ); - }, + yield actions.navigate( e.target.value, { + timeout: NAVIGATION_TIMEOUT, + } ); + } ), toggleAllTerms() { const context = getContext(); context.showAllTerms = ! context.showAllTerms; }, - *search( e ) { + search: withSyncEvent( function* ( e ) { e.preventDefault(); // Scope search to block context so multiple searchable query loops may coexist. const context = getContext(); @@ -47,6 +66,6 @@ store( 'query-filter', { context.searchValue = value; yield updateURL( action, value, name ); - }, + } ), }, } );