Bug: Expanded editor mount triggers flat→nested data migration / phantom dirty state (blockVersion 3)
Environment
|
|
| ACF PRO |
6.8.0.1 |
| WordPress |
6.9.4 |
| Block editor |
Post editor, iframe mode |
| Theme / setup |
Sage 11 / Acorn — ACF blocks registered via block.json with "blockVersion": 3 |
Summary
ACF PRO 6.8.0.1 fixed a phantom dirty-state bug where selecting a blockVersion: 3 block whose saved data attribute is in the v2 flat shape would trigger a re-serialization into the v3 nested shape, causing Gutenberg to mark the post as having unsaved changes.
That fix covers the selection code path only. The same bug still occurs on the expanded editor mount path: clicking the pencil icon in the block toolbar to open the slide-out sidebar causes ACF to migrate data from flat → nested shape. Because this differs from the saved serialization, Gutenberg marks the post dirty with zero user edits.
Steps to reproduce
- Register an ACF block with
"blockVersion": 3 in block.json.
- Create a new page, add the block, fill in a field, and save.
The DB now contains the flat v2 shape, e.g.:
"data": { "data_header": "test", "_data_header": "field_xxx", ... }
- Reload the editor.
- Click the block → no dirty state ✅ (6.8.0.1 fix working as expected)
- Click the pencil icon in the block toolbar to open the expanded editor → post is immediately marked dirty ❌
Expected behaviour
No changes to block data until the user actually edits a field.
Actual behaviour
The block's data attribute is rewritten from:
"data": { "data_header": "test", "_data_header": "field_xxx", ... }
to:
"data": { "field_parent": { "field_xxx": "test", ... } }
…with no user input. Gutenberg's dirty detection picks this up as an edit.
Diagnosis
The mutation originates inside acf-pro-blocks.min.js — the call that dirties the post resolves to the minified function be in that file, reached via a React setTimeout from the expanded-editor mount effect. It is the same flat→nested migration that 6.8.0.1 suppressed on selection; the expanded-editor mount path was not covered.
Console output (custom wp.data.subscribe listener watching isEditedPostDirty() and diffing getEditedPostContent())
[dirty] clean → dirty
Dirty entity records: [{kind: 'postType', name: 'page', key: 4179}]
Edits on postType/page/4179: {blocks: Array(1), selection: {…}, content: ƒ}
CONTENT string changed.
First diff at char 57
prev: <!-- wp:acf/statistics {"name":"acf/statistics","data":{"data_subheader":"test","_data_subheader":"field_5f2707babe67d",...
curr: <!-- wp:acf/statistics {"name":"acf/statistics","data":{"field_5f1a9980a7f2d":{"field_5f2707babe67d":"test",...
BLOCK attrs changed: true | META unchanged | Only dirty entity: post itself
Suggested fix
Whatever guard blockVersion: 3 introduced in 6.8.0.1 to prevent re-migrating already-loaded data on selection should be applied at the expanded-editor mount path as well – likely the mount effect inside the expanded sidebar component.
Bug: Expanded editor mount triggers flat→nested data migration / phantom dirty state (blockVersion 3)
Environment
block.jsonwith"blockVersion": 3Summary
ACF PRO 6.8.0.1 fixed a phantom dirty-state bug where selecting a
blockVersion: 3block whose saveddataattribute is in the v2 flat shape would trigger a re-serialization into the v3 nested shape, causing Gutenberg to mark the post as having unsaved changes.That fix covers the selection code path only. The same bug still occurs on the expanded editor mount path: clicking the pencil icon in the block toolbar to open the slide-out sidebar causes ACF to migrate data from flat → nested shape. Because this differs from the saved serialization, Gutenberg marks the post dirty with zero user edits.
Steps to reproduce
"blockVersion": 3inblock.json.The DB now contains the flat v2 shape, e.g.:
Expected behaviour
No changes to block data until the user actually edits a field.
Actual behaviour
The block's
dataattribute is rewritten from:to:
…with no user input. Gutenberg's dirty detection picks this up as an edit.
Diagnosis
The mutation originates inside
acf-pro-blocks.min.js— the call that dirties the post resolves to the minified functionbein that file, reached via a ReactsetTimeoutfrom the expanded-editor mount effect. It is the same flat→nested migration that 6.8.0.1 suppressed on selection; the expanded-editor mount path was not covered.Console output (custom
wp.data.subscribelistener watchingisEditedPostDirty()and diffinggetEditedPostContent())Suggested fix
Whatever guard
blockVersion: 3introduced in 6.8.0.1 to prevent re-migrating already-loaded data on selection should be applied at the expanded-editor mount path as well – likely the mount effect inside the expanded sidebar component.