From ea0bcb86902fddc60f805b4bee10a392122d379a Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Thu, 10 Sep 2026 20:01:22 -0700 Subject: [PATCH] perf(history): avoid temporary arrays in memory history --- .changeset/history-forward-entries.md | 5 +++ packages/history/src/index.ts | 10 +++--- .../history/tests/createMemoryHistory.test.ts | 32 +++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 .changeset/history-forward-entries.md diff --git a/.changeset/history-forward-entries.md b/.changeset/history-forward-entries.md new file mode 100644 index 00000000000..a4517e4c6a5 --- /dev/null +++ b/.changeset/history-forward-entries.md @@ -0,0 +1,5 @@ +--- +'@tanstack/history': patch +--- + +Avoid temporary arrays when replacing forward memory-history entries. diff --git a/packages/history/src/index.ts b/packages/history/src/index.ts index b0e006c80da..ae89e4aae08 100644 --- a/packages/history/src/index.ts +++ b/packages/history/src/index.ts @@ -663,13 +663,13 @@ export function createMemoryHistory( getLength: () => entries.length, pushState: (path, state) => { // Removes all subsequent entries after the current index to start a new branch + index = index < entries.length - 1 ? index + 1 : entries.length + states[index] = state + entries[index] = path if (index < entries.length - 1) { - entries.splice(index + 1) - states.splice(index + 1) + entries.length = index + 1 + states.length = index + 1 } - states.push(state) - entries.push(path) - index = Math.max(entries.length - 1, 0) }, replaceState: (path, state) => { states[index] = state diff --git a/packages/history/tests/createMemoryHistory.test.ts b/packages/history/tests/createMemoryHistory.test.ts index 11a0c269b34..c3162ab7de1 100644 --- a/packages/history/tests/createMemoryHistory.test.ts +++ b/packages/history/tests/createMemoryHistory.test.ts @@ -70,6 +70,38 @@ describe('createMemoryHistory', () => { expect(history.location.pathname).toBe('/b') }) + test('discards forward entries and their state when pushing a new branch', () => { + const initialEntries = ['/'] + const history = createMemoryHistory({ initialEntries }) + history.push('/kept', { marker: 'kept' }) + const keptState = history.location.state + history.push('/discarded-first', { marker: 'discarded-first' }) + history.push('/discarded-last', { marker: 'discarded-last' }) + history.go(-2) + history.push('/new', { marker: 'new' }) + + expect(initialEntries).toEqual(['/', '/kept', '/new']) + expect(history.length).toBe(3) + expect(history.location.pathname).toBe('/new') + expect(history.location.state).toMatchObject({ + __TSR_index: 2, + marker: 'new', + }) + const newState = history.location.state + + history.forward() + expect(history.location.pathname).toBe('/new') + expect(history.location.state).toBe(newState) + + history.back() + expect(history.location.pathname).toBe('/kept') + expect(history.location.state).toBe(keptState) + + history.forward() + expect(history.location.pathname).toBe('/new') + expect(history.location.state).toBe(newState) + }) + test('length', () => { const history = createMemoryHistory() expect(history.length).toBe(1)