diff --git a/packages/angular-table/src/injectTable.ts b/packages/angular-table/src/injectTable.ts index 94851485dd..a60e18910d 100644 --- a/packages/angular-table/src/injectTable.ts +++ b/packages/angular-table/src/injectTable.ts @@ -1,5 +1,6 @@ import { Injector, + NgZone, assertInInjectionContext, effect, inject, @@ -96,34 +97,37 @@ export function injectTable< ): AngularTable { assertInInjectionContext(injectTable) const injector = inject(Injector) + const ngZone = inject(NgZone) - return lazyInit(() => { - const table = constructTable({ - ...options(), - _features: { - coreReativityFeature: angularReactivity(injector), - ...options()._features, - }, - }) + return ngZone.runOutsideAngular(() => + lazyInit(() => { + const table = constructTable({ + ...options(), + _features: { + coreReativityFeature: angularReactivity(injector), + ...options()._features, + }, + }) - let isMount = true - effect( - () => { - const newOptions = options() - if (isMount) { - isMount = false - return - } - untracked(() => - table.setOptions((previous) => ({ - ...previous, - ...newOptions, - })), - ) - }, - { injector, debugName: 'tableOptionsUpdate' }, - ) + let isMount = true + effect( + () => { + const newOptions = options() + if (isMount) { + isMount = false + return + } + untracked(() => + table.setOptions((previous) => ({ + ...previous, + ...newOptions, + })), + ) + }, + { injector, debugName: 'tableOptionsUpdate' }, + ) - return table - }) + return table + }), + ) } diff --git a/packages/angular-table/src/reactivity.ts b/packages/angular-table/src/reactivity.ts index a4ad24799b..ad6907c124 100644 --- a/packages/angular-table/src/reactivity.ts +++ b/packages/angular-table/src/reactivity.ts @@ -1,4 +1,4 @@ -import { computed, signal, untracked } from '@angular/core' +import { NgZone, computed, signal, untracked } from '@angular/core' import { toObservable } from '@angular/core/rxjs-interop' import type { Atom, Observer, ReadonlyAtom } from '@tanstack/angular-store' import type { @@ -49,8 +49,10 @@ function signalToWritableAtom( * and `effect` calls. */ export function angularReactivity(injector: Injector): TableReactivityBindings { + const ngZone = injector.get(NgZone) return { createOptionsStore: true, + schedule: (fn) => ngZone.runOutsideAngular(() => queueMicrotask(fn)), createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { const signal = computed(() => fn(), { equal: options?.compare, diff --git a/packages/lit-table/src/reactivity.ts b/packages/lit-table/src/reactivity.ts index ccd158e7da..fb7d6c5d4f 100644 --- a/packages/lit-table/src/reactivity.ts +++ b/packages/lit-table/src/reactivity.ts @@ -13,6 +13,7 @@ import type { export function litReactivity(): TableReactivityBindings { return { createOptionsStore: true, + schedule: (fn) => queueMicrotask(() => fn()), batch, untrack: (fn) => fn(), createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { diff --git a/packages/preact-table/src/reactivity.ts b/packages/preact-table/src/reactivity.ts index 73d1813032..e362b691f4 100644 --- a/packages/preact-table/src/reactivity.ts +++ b/packages/preact-table/src/reactivity.ts @@ -13,6 +13,7 @@ import type { export function preactReactivity(): TableReactivityBindings { return { createOptionsStore: false, + schedule: (fn) => queueMicrotask(() => fn()), batch, untrack: (fn) => fn(), createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { diff --git a/packages/react-table/src/reactivity.ts b/packages/react-table/src/reactivity.ts index 65c55ce0da..34de5af37a 100644 --- a/packages/react-table/src/reactivity.ts +++ b/packages/react-table/src/reactivity.ts @@ -13,6 +13,7 @@ import type { export function reactReactivity(): TableReactivityBindings { return { createOptionsStore: false, + schedule: (fn) => queueMicrotask(fn), batch, untrack: (fn) => fn(), createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { diff --git a/packages/solid-table/src/reactivity.ts b/packages/solid-table/src/reactivity.ts index e06e631b1a..66571918cd 100644 --- a/packages/solid-table/src/reactivity.ts +++ b/packages/solid-table/src/reactivity.ts @@ -53,6 +53,7 @@ function signalToWritableAtom( export function solidReactivity(owner: Owner): TableReactivityBindings { return { createOptionsStore: true, + schedule: (fn) => queueMicrotask(() => fn()), createReadonlyAtom: (fn: () => T, options?: TableAtomOptions) => { const signal = createMemo(() => fn(), { equals: options?.compare, diff --git a/packages/svelte-table/src/reactivity.svelte.ts b/packages/svelte-table/src/reactivity.svelte.ts index 017595edbc..37887d6e2c 100644 --- a/packages/svelte-table/src/reactivity.svelte.ts +++ b/packages/svelte-table/src/reactivity.svelte.ts @@ -38,6 +38,7 @@ function subscribeToRune( export function svelteReactivity(): TableReactivityBindings { return { createOptionsStore: true, + schedule: (fn) => queueMicrotask(() => fn()), createReadonlyAtom: (fn: () => T, _options?: TableAtomOptions) => { const value = $derived.by(fn) diff --git a/packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts b/packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts index f59d3be92a..213bd3f7b9 100644 --- a/packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts +++ b/packages/table-core/src/core/reactivity/coreReactivityFeature.types.ts @@ -38,4 +38,8 @@ export interface TableReactivityBindings { * Batches reactive updates to avoid intermediate recomputation. */ batch: (fn: () => void) => void + /** + * Schedules a function to run. This is used to defer updates after the current call stack (render, etc.) has finished + */ + schedule: (fn: () => void) => void } diff --git a/packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts b/packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts index 325f8e3d83..4542ef6f48 100644 --- a/packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts +++ b/packages/table-core/src/features/row-expanding/rowExpandingFeature.utils.ts @@ -41,7 +41,7 @@ export function table_autoResetExpanded< table.options.autoResetExpanded ?? !table.options.manualExpanding ) { - queueMicrotask(() => table_resetExpanded(table)) + table._reactivity.schedule(() => table_resetExpanded(table)) } } diff --git a/packages/table-core/src/store-reactivity-bindings.ts b/packages/table-core/src/store-reactivity-bindings.ts index 197342eaae..3ad9a9b4d1 100644 --- a/packages/table-core/src/store-reactivity-bindings.ts +++ b/packages/table-core/src/store-reactivity-bindings.ts @@ -20,6 +20,7 @@ export function storeReactivityBindings(): TableReactivityBindings { return { createOptionsStore: true, batch, + schedule: (fn) => queueMicrotask(fn), untrack: (fn) => fn(), createReadonlyAtom: (fn, options) => { return createAtom(() => fn(), { diff --git a/packages/table-core/src/utils.ts b/packages/table-core/src/utils.ts index 4f1a02935b..20f2e2e7df 100755 --- a/packages/table-core/src/utils.ts +++ b/packages/table-core/src/utils.ts @@ -106,29 +106,6 @@ export function flattenBy( return flat } -/** - * Symbol used to attach internal memo metadata to wrapped functions. - * - * This is exported so diagnostics can recognize memoized functions without - * depending on a string property name. - */ -export const $internalMemoFnMeta = Symbol('memoFnMeta') -export type MemoFnMeta = { originalArgsLength?: number } - -/** - * @internal - */ -function setMemoFnMeta(fn: Function, meta: MemoFnMeta) { - Object.defineProperty(fn, $internalMemoFnMeta, { value: meta }) -} - -/** - * @internal - */ -export function getMemoFnMeta(fn: any): MemoFnMeta | null { - return (typeof fn === 'function' && fn[$internalMemoFnMeta]) ?? null -} - interface MemoOptions, TDepArgs, TResult> { fn: (...args: NoInfer) => TResult memoDeps?: (depArgs?: TDepArgs) => [...TDeps] | undefined @@ -178,8 +155,6 @@ export const memo = , TDepArgs, TResult>({ return result } - setMemoFnMeta(memoizedFn, { originalArgsLength: fn.length }) - return memoizedFn } @@ -283,6 +258,11 @@ export function tableMemo< console.groupEnd() } + const onAfterUpdateHandler = () => { + const { schedule, untrack } = table._reactivity + schedule(() => untrack(() => onAfterUpdate?.())) + } + const debugOptions = process.env.NODE_ENV === 'development' ? { @@ -313,12 +293,12 @@ export function tableMemo< Math.round((endCalcTime - startCalcTime) * 100) / 100 logTime(executionTime, true) } - queueMicrotask(() => onAfterUpdate?.()) + onAfterUpdateHandler() }, } : { onAfterUpdate: () => { - queueMicrotask(() => onAfterUpdate?.()) + onAfterUpdateHandler() }, } @@ -440,8 +420,6 @@ export function assignPrototypeAPIs< return fn(this, ...args) } } - - setMemoFnMeta(prototype[fnKey], { originalArgsLength: fn.length }) } } diff --git a/packages/vue-table/src/reactivity.ts b/packages/vue-table/src/reactivity.ts index 53952077a6..e04475636d 100644 --- a/packages/vue-table/src/reactivity.ts +++ b/packages/vue-table/src/reactivity.ts @@ -56,6 +56,7 @@ function refToWritableAtom(source: ShallowRef): Atom { export function vueReactivity(): TableReactivityBindings { return { createOptionsStore: true, + schedule: (fn) => queueMicrotask(() => fn()), createReadonlyAtom: (fn: () => T, _options?: TableAtomOptions) => { return refToReadonlyAtom(computed(fn)) },