d4ee09e54cd1a378305ec77d84ac2ed1b1314c7f
switchPage and deletePage ran side effects (loadState -> actions.deserialize, setActivePageId, activePageIdRef mutation) INSIDE setPages/setHeaderPage/ setFooterPage updater callbacks -- using the functional-updater form purely to peek at the latest `prev` value. React (in StrictMode dev builds) double-invokes updater functions passed to setState to catch exactly this kind of impurity, so every page switch deserialized the target's craft state twice. Add pagesRef/headerPageRef/footerPageRef mirroring the latest state on every render (same pattern already used for activePageIdRef), so switchPage/deletePage can read "what's current" synchronously in the event-handler body and run their side effects there -- after the state update is computed, not inside the updater. deletePage now passes a plain next-value to setPages instead of a function updater. Switching/deleting behavior (correct page loads, can't delete the last page) is unchanged. Verified the added test fails against the pre-fix code (deserialize called 2x) and passes against the fix (1x). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Description
Visual drag-and-drop website builder using GrapesJS
Languages
TypeScript
61.2%
HTML
17.1%
JavaScript
17%
CSS
3.6%
PHP
0.8%
Other
0.3%