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