From dad36416dcad6c4ce39e6415fe288f94cd4fdf1c Mon Sep 17 00:00:00 2001 From: Double Thinker <945606+double-thinker@users.noreply.github.com> Date: Thu, 15 Aug 2024 03:50:06 +0200 Subject: [PATCH] fix(storage): avoid calling setItem with the state just retrieved (#2678) --- src/middleware/persist.ts | 21 ++++++++++++++------- tests/persistSync.test.tsx | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/middleware/persist.ts b/src/middleware/persist.ts index fc8650034e..03ef7b4fe3 100644 --- a/src/middleware/persist.ts +++ b/src/middleware/persist.ts @@ -459,27 +459,34 @@ const newImpl: PersistImpl = (config, baseOptions) => (set, get, api) => { deserializedStorageValue.version !== options.version ) { if (options.migrate) { - return options.migrate( - deserializedStorageValue.state, - deserializedStorageValue.version, - ) + return [ + true, + options.migrate( + deserializedStorageValue.state, + deserializedStorageValue.version, + ), + ] as const } console.error( `State loaded from storage couldn't be migrated since no migrate function was provided`, ) } else { - return deserializedStorageValue.state + return [false, deserializedStorageValue.state] as const } } + return [false, undefined] as const }) - .then((migratedState) => { + .then((migrationResult) => { + const [migrated, migratedState] = migrationResult stateFromStorage = options.merge( migratedState as S, get() ?? configResult, ) set(stateFromStorage as S, true) - return setItem() + if (migrated) { + return setItem() + } }) .then(() => { // TODO: In the asynchronous case, it's possible that the state has changed diff --git a/tests/persistSync.test.tsx b/tests/persistSync.test.tsx index 396985e18e..5013bd6560 100644 --- a/tests/persistSync.test.tsx +++ b/tests/persistSync.test.tsx @@ -738,4 +738,26 @@ describe('persist middleware with sync configuration', () => { undefined, ) }) + + it('does not call setItem when hydrating from its own storage', async () => { + const setItem = vi.fn() + const storage = { + getItem: (name: string) => ({ + state: { count: 42, name }, + version: 0, + }), + setItem, + removeItem: () => {}, + } + + const useBoundStore = create( + persist(() => ({}), { + name: 'test-storage', + storage: storage, + }), + ) + + expect(useBoundStore.persist.hasHydrated()).toBe(true) + expect(setItem).toBeCalledTimes(0) + }) })