From a6e11b20655b581eb73e2925a1f1a6e593c85f43 Mon Sep 17 00:00:00 2001 From: devchangjun Date: Sat, 24 Jan 2026 17:17:06 +0900 Subject: [PATCH 1/3] fix(core): avoid Date.now() calls in memo when debug is disabled --- packages/table-core/src/utils.ts | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/table-core/src/utils.ts b/packages/table-core/src/utils.ts index c1f02a7c61..9e2012551a 100755 --- a/packages/table-core/src/utils.ts +++ b/packages/table-core/src/utils.ts @@ -147,7 +147,9 @@ export function memo( return (depArgs) => { let depTime: number - if (opts.key && opts.debug) depTime = Date.now() + const shouldDebug = opts.key && opts.debug?.() + + if (shouldDebug) depTime = Date.now() const newDeps = getDeps(depArgs) @@ -162,37 +164,35 @@ export function memo( deps = newDeps let resultTime: number - if (opts.key && opts.debug) resultTime = Date.now() + if (shouldDebug) resultTime = Date.now() result = fn(...newDeps) opts?.onChange?.(result) - if (opts.key && opts.debug) { - if (opts?.debug()) { - const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100 - const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100 - const resultFpsPercentage = resultEndTime / 16 - - const pad = (str: number | string, num: number) => { - str = String(str) - while (str.length < num) { - str = ' ' + str - } - return str + if (shouldDebug) { + const depEndTime = Math.round((Date.now() - depTime!) * 100) / 100 + const resultEndTime = Math.round((Date.now() - resultTime!) * 100) / 100 + const resultFpsPercentage = resultEndTime / 16 + + const pad = (str: number | string, num: number) => { + str = String(str) + while (str.length < num) { + str = ' ' + str } + return str + } - console.info( - `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`, - ` + console.info( + `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`, + ` font-size: .6rem; font-weight: bold; color: hsl(${Math.max( 0, Math.min(120 - 120 * resultFpsPercentage, 120), )}deg 100% 31%);`, - opts?.key, - ) - } + opts?.key, + ) } return result! From 9716d02063be28e79968340335c34c592bd3fde3 Mon Sep 17 00:00:00 2001 From: devchangjun Date: Sun, 25 Jan 2026 15:17:35 +0900 Subject: [PATCH 2/3] test(core): add regression test for memo debug timestamp --- packages/table-core/tests/memo.test.ts | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 packages/table-core/tests/memo.test.ts diff --git a/packages/table-core/tests/memo.test.ts b/packages/table-core/tests/memo.test.ts new file mode 100644 index 0000000000..9ff50c746e --- /dev/null +++ b/packages/table-core/tests/memo.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect, vi, afterEach } from 'vitest' +import { memo } from '../src/utils' + +describe('memo', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should not call Date.now() when debug is disabled', () => { + const dateSpy = vi.spyOn(Date, 'now') + const fn = vi.fn((a: number) => a * 2) + const getDeps = (a: number) => [a] + + // Test case 1: debug returns false + const memoizedFalse = memo(getDeps, fn, { + key: 'test', + debug: () => false, + }) + memoizedFalse(1) + expect(fn).toHaveBeenCalled() + expect(dateSpy).toHaveBeenCalledTimes(0) + }) + + it('should not call Date.now() when debug option is missing', () => { + const dateSpy = vi.spyOn(Date, 'now') + const fn = vi.fn((a: number) => a * 2) + const getDeps = (a: number) => [a] + + // Test case 2: no debug option + const memoizedNoDebug = memo(getDeps, fn, { + key: 'test', + }) + memoizedNoDebug(1) + expect(dateSpy).toHaveBeenCalledTimes(0) + }) + + it('should call Date.now() when debug is enabled', () => { + const dateSpy = vi.spyOn(Date, 'now') + const fn = vi.fn((a: number) => a * 2) + const getDeps = (a: number) => [a] + + // Test case 3: debug returns true + const memoizedTrue = memo(getDeps, fn, { + key: 'test', + debug: () => true, + }) + memoizedTrue(1) + expect(dateSpy).toHaveBeenCalled() + // It calls calling Date.now() multiple times in the debug logic (start, end, etc) + expect(dateSpy.mock.calls.length).toBeGreaterThan(0) + }) +}) From b7e453a9256a81c26a9ee4b26709e6e475ff540b Mon Sep 17 00:00:00 2001 From: devchangjun Date: Sun, 25 Jan 2026 15:24:29 +0900 Subject: [PATCH 3/3] fix(table-core): resolve type mismatch in memo tests --- packages/table-core/tests/memo.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/table-core/tests/memo.test.ts b/packages/table-core/tests/memo.test.ts index 9ff50c746e..1db2bee34d 100644 --- a/packages/table-core/tests/memo.test.ts +++ b/packages/table-core/tests/memo.test.ts @@ -9,7 +9,7 @@ describe('memo', () => { it('should not call Date.now() when debug is disabled', () => { const dateSpy = vi.spyOn(Date, 'now') const fn = vi.fn((a: number) => a * 2) - const getDeps = (a: number) => [a] + const getDeps = (a?: number) => [a as number] // Test case 1: debug returns false const memoizedFalse = memo(getDeps, fn, { @@ -24,7 +24,7 @@ describe('memo', () => { it('should not call Date.now() when debug option is missing', () => { const dateSpy = vi.spyOn(Date, 'now') const fn = vi.fn((a: number) => a * 2) - const getDeps = (a: number) => [a] + const getDeps = (a?: number) => [a as number] // Test case 2: no debug option const memoizedNoDebug = memo(getDeps, fn, { @@ -37,7 +37,7 @@ describe('memo', () => { it('should call Date.now() when debug is enabled', () => { const dateSpy = vi.spyOn(Date, 'now') const fn = vi.fn((a: number) => a * 2) - const getDeps = (a: number) => [a] + const getDeps = (a?: number) => [a as number] // Test case 3: debug returns true const memoizedTrue = memo(getDeps, fn, {