pmndrs / jotai

👻 Primitive and flexible state management for React
https://jotai.org
MIT License
18.25k stars 590 forks source link

App getting stuck when using loadable async atom in strict mode #1392

Closed SpringNyan closed 2 years ago

SpringNyan commented 2 years ago

Repro: https://codesandbox.io/s/8ez0zg

App getting stuck after click the button. If StrictMode component is removed, it works well.

Seems this issue is occurred after 1.7.8.

dai-shi commented 2 years ago

Simplified version: https://codesandbox.io/s/dark-currying-uebnpe?file=/src/App.js Issue confirmed without atomFamily. It doesn't happen without loadable.

Maybe #1091 caused this, but not sure.

Seems like a tough one.

dai-shi commented 2 years ago

We should put <StrictMode> in tests/utils/loadable.test.tsx.

diff ```diff diff --git a/tests/utils/loadable.test.tsx b/tests/utils/loadable.test.tsx index eb64c0f..b3c0f0e 100644 --- a/tests/utils/loadable.test.tsx +++ b/tests/utils/loadable.test.tsx @@ -1,4 +1,4 @@ -import { Suspense, useEffect } from 'react' +import { StrictMode, Suspense, useEffect } from 'react' import { fireEvent, render } from '@testing-library/react' import { Atom, atom, useAtomValue, useSetAtom } from 'jotai' import { loadable } from 'jotai/utils' @@ -13,9 +13,11 @@ it('loadable turns suspense into values', async () => { }) const { findByText } = render( + + ) await findByText('Loading...') @@ -30,9 +32,11 @@ it('loadable turns errors into values', async () => { }) const { findByText } = render( + + ) await findByText('Loading...') @@ -47,9 +51,11 @@ it('loadable turns primitive throws into values', async () => { }) const { findByText } = render( + + ) await findByText('Loading...') @@ -77,10 +83,12 @@ it('loadable goes back to loading after re-fetch', async () => { } const { findByText, getByText } = render( + + ) getByText('Loading...') @@ -116,10 +124,12 @@ it('loadable can recover from error', async () => { } const { findByText, getByText } = render( + + ) getByText('Loading...') @@ -136,9 +146,14 @@ it('loadable immediately resolves sync values', async () => { const effectCallback = jest.fn() const { getByText } = render( + - + + ) getByText('Data: 5') @@ -159,22 +174,26 @@ it('loadable can use resolved promises syncronously', async () => { } const { getByText, findByText, rerender } = render( + + ) await findByText('Ready') rerender( + + ) getByText('Data: 5') @@ -202,10 +221,12 @@ it('loadable of a derived async atom does not trigger infinite loop (#1114)', as } const { findByText, getByText } = render( + + ) getByText('Loading...') @@ -225,9 +246,11 @@ it('loadable of a derived async atom with error does not trigger infinite loop ( }) const { findByText, getByText } = render( + + ) getByText('Loading...') ```