From 0f34880c92fa3d770b0434d25668850624d54177 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 13:23:09 +0000 Subject: [PATCH] Defer off-screen Query Loop blocks to a viewport placeholder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rendering a Query Loop's edit component mounts the core block, which immediately fires a REST request to fetch posts for the preview. On a page with many query loops, every one of those requests is dispatched up front on editor load. Add a withViewportPlaceholder HOC (registered last, so it wraps the plugin's other core/query enhancements) that replaces an off-screen Query Loop with a cheap placeholder issuing no requests. An IntersectionObserver — built from the target node's own defaultView so it works whether or not the canvas is iframed — swaps in the real block once it nears the viewport (300px rootMargin preload). Selecting the block or one of its inner blocks renders it immediately, and once rendered a block stays rendered so scrolling away neither discards edits nor refetches. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01EAvqR2G3Q3aJkTtePzmLg7 --- CLAUDE.md | 4 + src/index.js | 152 +++++++++++++++++++++++- src/index.scss | 12 ++ tests/e2e/viewport-placeholder.spec.js | 154 +++++++++++++++++++++++++ 4 files changed, 317 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/viewport-placeholder.spec.js diff --git a/CLAUDE.md b/CLAUDE.md index 2706bc2..6c49285 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -61,6 +61,9 @@ The plugin handles two different query scenarios: - Global `$displayed_post_ids` array accumulates IDs from rendered query loops - Subsequent query loops with `excludeDisplayed` enabled filter out tracked IDs via `post__not_in` +### Editor Viewport Placeholder (Lazy Rendering) +`withViewportPlaceholder` HOC (registered last, so it wraps the plugin's other `core/query` enhancements) replaces off-screen Query Loop blocks with a cheap `` that fires no REST request. Mounting the real block triggers the core preview fetch, so on a page with many query loops this defers those requests until each block scrolls near the viewport. An `IntersectionObserver` — constructed from the target node's own `ownerDocument.defaultView` so it works whether or not the canvas is iframed — swaps in the real block on intersection (with a 300px `rootMargin` preload). Selecting a block (e.g. right after insertion or via List View) renders it immediately, and once rendered a block stays rendered (latched via state) so scrolling away neither discards edits nor refetches. + ### Editor Preview Synchronization `withPostTemplateStyles` HOC injects an inline `