diff --git a/tests/react/async2.test.tsx b/tests/react/async2.test.tsx index 2796d3a260..f68ed4e10c 100644 --- a/tests/react/async2.test.tsx +++ b/tests/react/async2.test.tsx @@ -380,3 +380,37 @@ describe('write to async atom twice', async () => { await screen.findByText('count: 4') }) }) + +describe('with onMount', () => { + it('does not infinite loop with setting a promise (#2931)', async () => { + const firstPromise = Promise.resolve(1) + const secondPromise = Promise.resolve(2) + const asyncAtom = atom(firstPromise) + asyncAtom.onMount = (setCount) => { + setCount((prev) => (prev === firstPromise ? secondPromise : prev)) + } + const Component = () => { + const [count, setCount] = useAtom(asyncAtom) + return ( + <> +
count: {count}
+ + + ) + } + await act(async () => { + render( + + + + + , + ) + }) + await screen.findByText('count: 2') + await userEvent.click(screen.getByText('button')) + await screen.findByText('count: 3') + }) +}) diff --git a/tests/react/vanilla-utils/atomWithStorage.test.tsx b/tests/react/vanilla-utils/atomWithStorage.test.tsx index b705328e43..5a58323513 100644 --- a/tests/react/vanilla-utils/atomWithStorage.test.tsx +++ b/tests/react/vanilla-utils/atomWithStorage.test.tsx @@ -745,3 +745,51 @@ describe('with subscribe method in string storage', () => { // expect(storageData.count).toBe('11') }) }) + +describe('with custom async storage', () => { + it('does not infinite loop (#2931)', async () => { + let storedValue = 0 + let cachedPromise: + | [typeof storedValue, Promise] + | null = null + const counterAtom = atomWithStorage('counter', 0, { + getItem(_key: string, _initialValue: number) { + if (cachedPromise && cachedPromise[0] === storedValue) { + return cachedPromise[1] + } + const promise = Promise.resolve(storedValue) + cachedPromise = [storedValue, promise] + return promise + }, + async setItem(_key, newValue) { + storedValue = await new Promise((resolve) => resolve(newValue)) + }, + async removeItem() {}, + }) + const Component = () => { + const [count, setCount] = useAtom(counterAtom) + return ( + <> +
count: {count}
+ + + ) + } + await act(async () => { + render( + + + + + , + ) + }) + await screen.findByText('count: 0') + await userEvent.click(screen.getByText('button')) + await screen.findByText('count: 1') + await userEvent.click(screen.getByText('button')) + await screen.findByText('count: 2') + }) +})