Skip to content

Fix false "unsaved changes" warning from Query Loop queryId churn on load#23

Merged
mattheu merged 4 commits into
mainfrom
fix/register-hmqueryloop-attribute-server-side
Jul 7, 2026
Merged

Fix false "unsaved changes" warning from Query Loop queryId churn on load#23
mattheu merged 4 commits into
mainfrom
fix/register-hmqueryloop-attribute-server-side

Conversation

@michelhm

@michelhm michelhm commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Problem

Opening any page/post that contains Query Loop blocks in the editor immediately shows the "Leave site? Changes that you made may not be saved." warning — before the user makes any edit. isEditedPostDirty() returns true on load, and WordPress fires continuous background autosaves as a side effect.

Root cause

withUniqueQueryId (src/index.js) forced every core/query block's queryId to a strictly index-based value (postId * 1000 + index + 1) on load, rewriting it via setAttributes whenever it didn't match:

const expectedQueryId = postId * 1000 + index + 1;
useEffect( () => {
  if ( attributes.queryId !== expectedQueryId ) {
    setAttributes( { queryId: expectedQueryId } );
  }
} );

For any page whose saved queryIds aren't perfectly sequential — e.g. after a query block was deleted or reordered, leaving a gap or a duplicate — this rewrites the ids on every editor open. Setting an attribute (even to a value that leaves the serialized markup unchanged) records a block edit → the post is marked dirty on load → the "Leave site?" warning and endless autosaves. It never converges, because the recomputed ids keep disagreeing with what's stored.

Fix

Only assign a new queryId when the current one is missing or already used by an earlier query block; leave already-unique ids untouched. This:

  • makes the HOC idempotent — unique ids are never rewritten, so there's no false "dirty" on load;
  • still deduplicates genuine collisions (e.g. from copy-pasting a query block) — the earlier occurrence keeps its id, later duplicates get a fresh unique one;
  • mirrors the plugin's own server-side deduplicate_query_ids(), which already only overrides a queryId when it's a duplicate ("preserves the original value when possible… improving interoperability"). The editor was the inconsistent side.

The change is editor-only — front-end rendering continues to use the untouched server-side dedup, so published sites are unaffected.

Testing

  • Reproduced and fixed on a production block theme (~11 query loops with a gapped/duplicated queryId): isEditedPostDirty() goes truefalse on load; no warning; no autosave churn; queryIds stay stable across reloads.
  • Plugin e2e suite: unique-query-id and all other relevant tests pass. The 2 exclude-with-post-in failures are pre-existing on main (the test env auto-pulls Advanced Query Loop's latest zip, whose post-selection UI changed and no longer matches the test selector) — unrelated to this change.

Note

This supersedes the earlier approach in this PR (registering the hmQueryLoop attribute server-side). That was based on an incorrect diagnosis and did not fix the issue; the real cause is the queryId churn above. The branch history was rewritten to a single focused commit. (Branch name is now a cosmetic mismatch.)

@michelhm michelhm self-assigned this Jul 3, 2026
@michelhm michelhm requested a review from mattheu July 3, 2026 14:23
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Playwright test results

passed  19 passed

Details

stats  19 tests across 6 suites
duration  1 minute, 16 seconds
commit  67871ec

@michelhm michelhm marked this pull request as draft July 3, 2026 14:56
withUniqueQueryId forced every core/query block's queryId to a strictly
index-based value (postId*1000 + index + 1) on load, rewriting it via
setAttributes whenever it didn't match. For any page whose saved queryIds are
not perfectly sequential — e.g. after a query block is deleted or reordered,
leaving a gap or a duplicate — this rewrote the ids on every editor open,
marking the post dirty before the user made any change. That surfaced a
"Leave site?" warning on load and triggered continuous background autosaves.

Only assign a new queryId when the current one is missing or already used by an
earlier query block; leave already-unique ids untouched. This mirrors the
server-side deduplicate_query_ids() behaviour, is idempotent, and still fixes
genuinely duplicated ids (e.g. from copy-pasting a query block).
@michelhm michelhm force-pushed the fix/register-hmqueryloop-attribute-server-side branch from b81685f to 64d4c9f Compare July 3, 2026 16:03
@michelhm michelhm changed the title Register hmQueryLoop attribute server-side to fix false unsaved-changes warning Fix false "unsaved changes" warning from Query Loop queryId churn on load Jul 3, 2026
@michelhm

michelhm commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Heads-up on a change of approach since the last review 👇

After testing the original fix (registering the hmQueryLoop attribute server-side) against a real block-theme site, it turned out not to fix the false "unsaved changes" — the earlier "it works" was a post-save artifact. Deeper investigation traced the real cause to withUniqueQueryId rewriting queryIds on every load (details in the updated description).

I've rewritten the branch to a single focused commit with the real fix, and updated the title/description accordingly. The fix mirrors the existing server-side deduplicate_query_ids() and is editor-only, so front-end rendering is untouched. Verified on a production theme and against the e2e suite (the 2 exclude-with-post-in failures are pre-existing on main from the Advanced Query Loop test-env drift, unrelated to this change).

Re-review appreciated when you get a chance 🙏

@michelhm michelhm marked this pull request as ready for review July 3, 2026 16:05
@mattheu

mattheu commented Jul 4, 2026

Copy link
Copy Markdown
Member

Updated to add a test for this case

@mattheu mattheu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems good to me. @roborourke Would love a second pair of eyes though if you have a chance.

@roborourke roborourke left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, we can perhaps lose the console statements in the test but it's not a blocker for me.

Removed console.log statements for query IDs and editor state checks.
@mattheu mattheu merged commit 7c881b2 into main Jul 7, 2026
1 check passed
@mattheu mattheu deleted the fix/register-hmqueryloop-attribute-server-side branch July 7, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants