Summary
In the Gutenberg block integration (gutenberg/index.js), an initial render lifecycle bug causes clean posts, templates, and template parts to be marked as modified ("Review changes") immediately upon opening the WordPress Site Editor or Block Editor.
Affected surface
Gutenberg
Steps to reproduce
- Install Core Framework and enable the Gutenberg integration.
- Open any page or template part in the Site Editor (
site-editor.php).
- Observe that without clicking or typing anything, the editor shows unsaved changes ("Review 1 change").
- Inspecting
wp.data.select('core').getEntityRecordNonTransientEdits(...) shows that block className attributes were modified during initial page load.
Expected behavior
Opening a clean page, template, or template part in the editor should not trigger unsaved changes or mark blocks modified until the user actually interacts with the controls or edits content.
Actual behavior
Opening any page or template immediately flags blocks as modified. A mount useEffect in gutenberg/index.js fires on initial render before state populates, calling setAttributes({ className: "" }) and wiping/rewriting classes in Gutenberg's in-memory store.
Core Framework version or commit
2.0.2
Environment
WordPress 7.1, PHP 8.4.23, Gutenberg / Site Editor (FSE), Chrome (any modern browser)
Logs or screenshots
// Root cause in gutenberg/index.js:
const [h, m] = useState({ active: [], dynamic: [] });
// Because h initializes as empty ({ active: [], dynamic: [] }), this effect runs on initial mount:
useEffect(() => {
r.setAttributes({ className: ... });
}, [h]);
// This mutates Gutenberg's entity record during initial render, creating spurious unsaved changes.
// Fix: Remove this useEffect. Sidebar click handlers already dispatch setAttributes() directly when the user toggles classes.
Root cause
In gutenberg/index.js, the component initializes internal state with empty arrays:
const [h, m] = useState({ active: [], dynamic: [] });
A useEffect hook depends on [h] and synchronizes it to Gutenberg via r.setAttributes(...):
javascript
useEffect(() => {
// On mount, h is empty ({ active: [], dynamic: [] })
// This evaluates to an empty string and calls setAttributes({ className: "" })
// before the actual classes populate, dirtying the block on mount.
r.setAttributes({ className: ... });
}, [h]);
Because h starts empty, this effect fires on initial mount and writes to the block attributes before the saved classes finish loading. Calling setAttributes() during mount mutates Gutenberg's entity store and creates spurious unsaved changes.
Furthermore, this mount effect is redundant because Core Framework's click handlers already call setAttributes() directly when a user interacts with class controls in the sidebar.
Proposed fix
Remove the useEffect that calls setAttributes based on [h]. Class updates should only be dispatched when the user explicitly clicks or toggles a class in the inspector panel.
Affected file:
wp-content/plugins/core-framework/gutenberg/index.js (or source file before build)
Summary
In the Gutenberg block integration (
gutenberg/index.js), an initial render lifecycle bug causes clean posts, templates, and template parts to be marked as modified ("Review changes") immediately upon opening the WordPress Site Editor or Block Editor.Affected surface
Gutenberg
Steps to reproduce
site-editor.php).wp.data.select('core').getEntityRecordNonTransientEdits(...)shows that blockclassNameattributes were modified during initial page load.Expected behavior
Opening a clean page, template, or template part in the editor should not trigger unsaved changes or mark blocks modified until the user actually interacts with the controls or edits content.
Actual behavior
Opening any page or template immediately flags blocks as modified. A mount
useEffectingutenberg/index.jsfires on initial render before state populates, callingsetAttributes({ className: "" })and wiping/rewriting classes in Gutenberg's in-memory store.Core Framework version or commit
2.0.2
Environment
WordPress 7.1, PHP 8.4.23, Gutenberg / Site Editor (FSE), Chrome (any modern browser)
Logs or screenshots
// Root cause in gutenberg/index.js:
const [h, m] = useState({ active: [], dynamic: [] });
// Because h initializes as empty ({ active: [], dynamic: [] }), this effect runs on initial mount:
useEffect(() => {
r.setAttributes({ className: ... });
}, [h]);
// This mutates Gutenberg's entity record during initial render, creating spurious unsaved changes.
// Fix: Remove this useEffect. Sidebar click handlers already dispatch setAttributes() directly when the user toggles classes.
Root cause
In
gutenberg/index.js, the component initializes internal state with empty arrays: