Repository: facebook/react
File: packages/react-reconciler/src/__tests__/ReactUse-test.js (JavaScript)

Code before:
        root.render(<App>{iterable}</App>);
      });
    });
    assertLog(['Async text requested [Hi]']);

    await expect(async () => {
      await act(() => resolveTextRequests('Hi'));
    }).toErrorDev(
      // We get this warning because the generator's promise themselves are not cached.
      'A component was suspended by an uncached promise. Creating ' +
        'promises inside a Client Component or hook is not yet ' +
        'supported, except via a Suspense-compatible library or framework.',
    );

    assertLog(['Async text requested [World]']);

    await act(() => resolveTextRequests('World'));

    assertLog(['Hi', 'World']);
    expect(root).toMatchRenderedOutput(<div>Hi World</div>);
  });

  it('regression: does not get stuck in pending state after `use` suspends', async () => {
    // This is a regression test. The root cause was an issue where we failed to
    // switch from the "re-render" dispatcher back to the "update" dispatcher
    // after a `use` suspends and triggers a replay.
    let update;
    function App({promise}) {
      useState(false);

      const value = use(promise);

      const [isPending, startLocalTransition] = useTransition();
      update = () => {
        startLocalTransition(() => {
          root.render(<App promise={getAsyncText('Updated')} />);
        });
      };

      return <Text text={value + (isPending ? ' (pending...)' : '')} />;
    }

    const root = ReactNoop.createRoot();
    await act(() => {
      root.render(<App promise={Promise.resolve('Initial')} />);
    });
    assertLog(['Initial']);
    expect(root).toMatchRenderedOutput('Initial');

    await act(() => update());
    assertLog(['Async text requested [Updated]', 'Initial (pending...)']);

Review comment (suggestion):
Now it also makes sense why it didn't get stuck if we called `useTransition` before `use`. Thanks for the explainer.

Code after:
        root.render(<App>{iterable}</App>);
      });
    });
    assertLog(['Async text requested [Hi]']);

    await expect(async () => {
      await act(() => resolveTextRequests('Hi'));
    }).toErrorDev(
      // We get this warning because the generator's promise themselves are not cached.
      'A component was suspended by an uncached promise. Creating ' +
        'promises inside a Client Component or hook is not yet ' +
        'supported, except via a Suspense-compatible library or framework.',
    );

    assertLog(['Async text requested [World]']);

    await act(() => resolveTextRequests('World'));

    assertLog(['Hi', 'World']);
    expect(root).toMatchRenderedOutput(<div>Hi World</div>);
  });

  it(
    'regression: does not get stuck in pending state after `use` suspends ' +
      '(when `use` comes before all hooks)',
    async () => {
      // This is a regression test. The root cause was an issue where we failed to
      // switch from the "re-render" dispatcher back to the "update" dispatcher
      // after a `use` suspends and triggers a replay.
      let update;
      function App({promise}) {
        const value = use(promise);

        const [isPending, startLocalTransition] = useTransition();
        update = () => {
          startLocalTransition(() => {
            root.render(<App promise={getAsyncText('Updated')} />);
          });
        };

        return <Text text={value + (isPending ? ' (pending...)' : '')} />;
      }

      const root = ReactNoop.createRoot();
      await act(() => {
        root.render(<App promise={Promise.resolve('Initial')} />);
      });
      assertLog(['Initial']);
      expect(root).toMatchRenderedOutput('Initial');

      await act(() => update());