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