Skip to content

Commit b614085

Browse files
fix(tables): resolve undo layout ownership at write time, not dispatch time
The layout writes for column create/delete happen in mutation success callbacks, and persistLayoutRef is rebound every render. Resolving entryOwnsLayout up front meant the guard could still hold from before a view switch while the sink it guarded already pointed at the destination, writing the recorded view's layout into whichever view was now active. Now a function, so the guard and the sink are read at the same moment: switch away mid-mutation and the schema half still lands while the layout half is dropped, matching the rule that undo only ever affects the view on screen.
1 parent 7d86bd3 commit b614085

1 file changed

Lines changed: 10 additions & 5 deletions

File tree

apps/sim/hooks/use-table-undo.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,12 @@ export function useTableUndo({
194194
// switch — but the layout they recorded belongs to the view that was active
195195
// at the time. Replaying it elsewhere would write one view's order/widths
196196
// into another, so the schema half runs and the layout half is dropped.
197-
const entryOwnsLayout = entryViewId === activeViewIdRef.current
197+
//
198+
// Evaluated at CALL time, not here: these writes happen in mutation success
199+
// callbacks, and `persistLayoutRef` is rebound on every render. A guard
200+
// resolved up front would still hold from before a switch while the sink it
201+
// guards already pointed at the destination view.
202+
const entryOwnsLayout = () => entryViewId === activeViewIdRef.current
198203
try {
199204
switch (action.type) {
200205
case 'update-cell': {
@@ -343,7 +348,7 @@ export function useTableUndo({
343348
metadata.pinnedColumns = newPinned
344349
}
345350
if (Object.keys(metadata).length > 0) {
346-
if (entryOwnsLayout) persistLayoutRef.current(metadata)
351+
if (entryOwnsLayout()) persistLayoutRef.current(metadata)
347352
}
348353
},
349354
})
@@ -435,7 +440,7 @@ export function useTableUndo({
435440
}
436441
}
437442
if (Object.keys(metadata).length > 0) {
438-
if (entryOwnsLayout) persistLayoutRef.current(metadata)
443+
if (entryOwnsLayout()) persistLayoutRef.current(metadata)
439444
}
440445
},
441446
}
@@ -464,7 +469,7 @@ export function useTableUndo({
464469
}
465470
}
466471
if (Object.keys(metadata).length > 0) {
467-
if (entryOwnsLayout) persistLayoutRef.current(metadata)
472+
if (entryOwnsLayout()) persistLayoutRef.current(metadata)
468473
}
469474
},
470475
})
@@ -529,7 +534,7 @@ export function useTableUndo({
529534
// Pruning already drops these on a view switch, so a mismatch here
530535
// should be unreachable; the guard keeps the invariant local rather
531536
// than dependent on when the prune effect happens to run.
532-
if (!entryOwnsLayout) break
537+
if (!entryOwnsLayout()) break
533538
onColumnOrderChangeRef.current?.(order)
534539
persistLayoutRef.current({ columnOrder: order })
535540
break

0 commit comments

Comments
 (0)