Layer Rename Does Not Persist After the Next Panel Rebuild
TLDR
Layer name updates that appear to succeed in the UI but revert back to old values on the next canvas rebuild stem from asynchronous race conditions. Attempting to synchronizeid and data-layer-name attributes using a MutationObserver fails because MutationObserver callbacks execute as microtasks, firing after the synchronous DOM rebuild pass reads the old attribute value. Updating both attributes synchronously at edit time fixes the persistence bug.
| Mutation Approach | Execution Timing | Rebuild State Read | Outcome |
|---|---|---|---|
MutationObserver Microtask | Asynchronous Microtask | Reads old data-layer-name before write | Reverts to old name (BUG) |
| Synchronous Attribute Assignment | Synchronous Execution | Reads updated data-layer-name value | 100% Persistent Layer Rename |
Problem statement: the temporary layer rename glitch
Users double-clicking a layer row to rename an element (e.g., changing "Resistor-1" to "Power Resistor R1") saw the new name render in the layer panel.
However, as soon as any subsequent canvas action occurred (such as dragging a component or adding a shape), the layer panel rebuilt itself and reverted back to the original name.
Technical failure mode: microtask race conditions in MutationObservers
The original implementation attempted to keep element id and data-layer-name attributes in sync via a MutationObserver:
// DEFECTIVE IMPLEMENTATION: Observer callback executes AFTER synchronous rebuild!
function renameElement(el, newName) {
const obs = new MutationObserver(() => {
el.setAttribute('data-layer-name', el.id.replace(/-/g, ' '));
obs.disconnect();
});
obs.observe(el, { attributes: true, attributeFilter: ['id'] });
el.id = newName.replace(/\s+/g, '-').toLowerCase(); // ID assignment triggers observer // A canvas rebuild triggered in the same synchronous turn reads 'data-layer-name' // BEFORE the MutationObserver microtask callback executes! }
Because MutationObserver tasks execute asynchronously on the microtask queue, a synchronous canvas rebuild pass running in the same turn reads data-layer-name before the observer updates it, rendering the old attribute value.
The fix: synchronous attribute assignment
Eliminate the MutationObserver and update both attributes synchronously at the moment of user entry:
// REFACTORED: Synchronous attribute assignment
function renameElement(el, newName) {
const safeId = newName.trim().replace(/\s+/g, '-').toLowerCase();
el.id = safeId;
el.setAttribute('data-layer-name', newName.trim()); // Synchronous write!
// Instant DOM-derived panel refresh guarantees attribute parity buildLayerPanel(contentRoot, panelContainer); }
Rule of thumb: Never use asynchronous MutationObserver tasks to synchronize two attributes under your direct control. Perform attribute updates synchronously to prevent microtask race conditions during UI rebuilds.