Skip to content

Commit f829733

Browse files
eps1lonacdlite
andcommitted
Add regression test for stuck pending state
Introduces a regression test for a bug where the pending state of a useTransition hook is not set back to `false` if it comes after a `use` that suspended. Co-authored-by: Andrew Clark <[email protected]>
1 parent 9d4fba0 commit f829733

File tree

1 file changed

+39
-0
lines changed

1 file changed

+39
-0
lines changed

packages/react-reconciler/src/__tests__/ReactUse-test.js

+39
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ let act;
1616
let use;
1717
let useDebugValue;
1818
let useState;
19+
let useTransition;
1920
let useMemo;
2021
let useEffect;
22+
let useOptimistic;
2123
let Suspense;
2224
let startTransition;
2325
let pendingTextRequests;
@@ -38,8 +40,10 @@ describe('ReactUse', () => {
3840
use = React.use;
3941
useDebugValue = React.useDebugValue;
4042
useState = React.useState;
43+
useTransition = React.useTransition;
4144
useMemo = React.useMemo;
4245
useEffect = React.useEffect;
46+
useOptimistic = React.useOptimistic;
4347
Suspense = React.Suspense;
4448
startTransition = React.startTransition;
4549

@@ -1915,4 +1919,39 @@ describe('ReactUse', () => {
19151919
assertLog(['Hi', 'World']);
19161920
expect(root).toMatchRenderedOutput(<div>Hi World</div>);
19171921
});
1922+
1923+
it('regression: does not get stuck in pending state after `use` suspends', async () => {
1924+
// This is a regression test. The root cause was an issue where we failed to
1925+
// switch from the "re-render" dispatcher back to the "update" dispatcher
1926+
// after a `use` suspends and triggers a replay.
1927+
let update;
1928+
function App({promise}) {
1929+
useState(false);
1930+
1931+
const value = use(promise);
1932+
1933+
const [isPending, startLocalTransition] = useTransition();
1934+
update = () => {
1935+
startLocalTransition(() => {
1936+
root.render(<App promise={getAsyncText('Updated')} />);
1937+
});
1938+
};
1939+
1940+
return <Text text={value + (isPending ? ' (pending...)' : '')} />;
1941+
}
1942+
1943+
const root = ReactNoop.createRoot();
1944+
await act(() => {
1945+
root.render(<App promise={Promise.resolve('Initial')} />);
1946+
});
1947+
assertLog(['Initial']);
1948+
expect(root).toMatchRenderedOutput('Initial');
1949+
1950+
await act(() => update());
1951+
assertLog(['Async text requested [Updated]', 'Initial (pending...)']);
1952+
1953+
await act(() => resolveTextRequests('Updated'));
1954+
assertLog(['Updated']);
1955+
expect(root).toMatchRenderedOutput('Updated');
1956+
});
19181957
});

0 commit comments

Comments
 (0)