Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c13ea86

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 ec6fe57 commit c13ea86

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

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

Lines changed: 80 additions & 0 deletions
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,80 @@ describe('ReactUse', () => {
19151919
assertLog(['Hi', 'World']);
19161920
expect(root).toMatchRenderedOutput(<div>Hi World</div>);
19171921
});
1922+
1923+
it(
1924+
'regression: does not get stuck in pending state after `use` suspends ' +
1925+
'(when `use` comes before all hooks)',
1926+
async () => {
1927+
// This is a regression test. The root cause was an issue where we failed to
1928+
// switch from the "re-render" dispatcher back to the "update" dispatcher
1929+
// after a `use` suspends and triggers a replay.
1930+
let update;
1931+
function App({promise}) {
1932+
const value = use(promise);
1933+
1934+
const [isPending, startLocalTransition] = useTransition();
1935+
update = () => {
1936+
startLocalTransition(() => {
1937+
root.render(<App promise={getAsyncText('Updated')} />);
1938+
});
1939+
};
1940+
1941+
return <Text text={value + (isPending ? ' (pending...)' : '')} />;
1942+
}
1943+
1944+
const root = ReactNoop.createRoot();
1945+
await act(() => {
1946+
root.render(<App promise={Promise.resolve('Initial')} />);
1947+
});
1948+
assertLog(['Initial']);
1949+
expect(root).toMatchRenderedOutput('Initial');
1950+
1951+
await act(() => update());
1952+
assertLog(['Async text requested [Updated]', 'Initial (pending...)']);
1953+
1954+
await act(() => resolveTextRequests('Updated'));
1955+
assertLog(['Updated']);
1956+
expect(root).toMatchRenderedOutput('Updated');
1957+
},
1958+
);
1959+
1960+
it(
1961+
'regression: does not get stuck in pending state after `use` suspends ' +
1962+
'(when `use` in in the middle of hook list)',
1963+
async () => {
1964+
// Same as previous test but `use` comes in between two hooks.
1965+
let update;
1966+
function App({promise}) {
1967+
// This hook is only here to test that `use` resumes correctly after
1968+
// suspended even if it comes in between other hooks.
1969+
useState(false);
1970+
1971+
const value = use(promise);
1972+
1973+
const [isPending, startLocalTransition] = useTransition();
1974+
update = () => {
1975+
startLocalTransition(() => {
1976+
root.render(<App promise={getAsyncText('Updated')} />);
1977+
});
1978+
};
1979+
1980+
return <Text text={value + (isPending ? ' (pending...)' : '')} />;
1981+
}
1982+
1983+
const root = ReactNoop.createRoot();
1984+
await act(() => {
1985+
root.render(<App promise={Promise.resolve('Initial')} />);
1986+
});
1987+
assertLog(['Initial']);
1988+
expect(root).toMatchRenderedOutput('Initial');
1989+
1990+
await act(() => update());
1991+
assertLog(['Async text requested [Updated]', 'Initial (pending...)']);
1992+
1993+
await act(() => resolveTextRequests('Updated'));
1994+
assertLog(['Updated']);
1995+
expect(root).toMatchRenderedOutput('Updated');
1996+
},
1997+
);
19181998
});

0 commit comments

Comments
 (0)