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) + }) })