Codemod act -> await act (3/?) (#26336)

Similar to the rationale for `waitFor` (see #26285), we should always
await the result of an `act` call so that microtasks have a chance to
fire.

This only affects the internal `act` that we use in our repo, for now.
In the public `act` API, we don't yet require this; however, we
effectively will for any update that triggers suspense once `use` lands.
So we likely will start warning in an upcoming minor.
This commit is contained in:
Andrew Clark
2023-03-07 14:39:30 -05:00
committed by GitHub
parent 58605f7988
commit 161f6ae42c
11 changed files with 179 additions and 163 deletions

View File

@@ -36,7 +36,7 @@ describe('StrictEffectsMode', () => {
);
}
it('should not double invoke effects in legacy mode', () => {
it('should not double invoke effects in legacy mode', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect mount');
@@ -51,14 +51,14 @@ describe('StrictEffectsMode', () => {
return text;
}
act(() => {
await act(async () => {
ReactTestRenderer.create(<App text={'mount'} />);
});
assertLog(['useLayoutEffect mount', 'useEffect mount']);
});
it('double invoking for effects works properly', () => {
it('double invoking for effects works properly', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect mount');
@@ -74,7 +74,7 @@ describe('StrictEffectsMode', () => {
}
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<App text={'mount'} />, {
unstable_isConcurrent: true,
});
@@ -93,7 +93,7 @@ describe('StrictEffectsMode', () => {
assertLog(['useLayoutEffect mount', 'useEffect mount']);
}
act(() => {
await act(async () => {
renderer.update(<App text={'update'} />);
});
@@ -104,14 +104,14 @@ describe('StrictEffectsMode', () => {
'useEffect mount',
]);
act(() => {
await act(async () => {
renderer.unmount();
});
assertLog(['useLayoutEffect unmount', 'useEffect unmount']);
});
it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect One mount');
@@ -127,7 +127,7 @@ describe('StrictEffectsMode', () => {
}
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<App text={'mount'} />, {
unstable_isConcurrent: true,
});
@@ -146,7 +146,7 @@ describe('StrictEffectsMode', () => {
assertLog(['useEffect One mount', 'useEffect Two mount']);
}
act(() => {
await act(async () => {
renderer.update(<App text={'update'} />);
});
@@ -157,14 +157,14 @@ describe('StrictEffectsMode', () => {
'useEffect Two mount',
]);
act(() => {
await act(async () => {
renderer.unmount(null);
});
assertLog(['useEffect One unmount', 'useEffect Two unmount']);
});
it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
function App({text}) {
React.useLayoutEffect(() => {
Scheduler.log('useLayoutEffect One mount');
@@ -180,7 +180,7 @@ describe('StrictEffectsMode', () => {
}
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<App text={'mount'} />, {
unstable_isConcurrent: true,
});
@@ -199,7 +199,7 @@ describe('StrictEffectsMode', () => {
assertLog(['useLayoutEffect One mount', 'useLayoutEffect Two mount']);
}
act(() => {
await act(async () => {
renderer.update(<App text={'update'} />);
});
@@ -210,14 +210,14 @@ describe('StrictEffectsMode', () => {
'useLayoutEffect Two mount',
]);
act(() => {
await act(async () => {
renderer.unmount();
});
assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']);
});
it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {
it('useEffect and useLayoutEffect is called twice when there is no unmount', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect mount');
@@ -231,7 +231,7 @@ describe('StrictEffectsMode', () => {
}
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<App text={'mount'} />, {
unstable_isConcurrent: true,
});
@@ -248,20 +248,20 @@ describe('StrictEffectsMode', () => {
assertLog(['useLayoutEffect mount', 'useEffect mount']);
}
act(() => {
await act(async () => {
renderer.update(<App text={'update'} />);
});
assertLog(['useLayoutEffect mount', 'useEffect mount']);
act(() => {
await act(async () => {
renderer.unmount();
});
assertLog([]);
});
it('passes the right context to class component lifecycles', () => {
it('passes the right context to class component lifecycles', async () => {
class App extends React.PureComponent {
test() {}
@@ -285,7 +285,7 @@ describe('StrictEffectsMode', () => {
}
}
act(() => {
await act(async () => {
ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});
});
@@ -300,7 +300,7 @@ describe('StrictEffectsMode', () => {
}
});
it('double invoking works for class components', () => {
it('double invoking works for class components', async () => {
class App extends React.PureComponent {
componentDidMount() {
Scheduler.log('componentDidMount');
@@ -320,7 +320,7 @@ describe('StrictEffectsMode', () => {
}
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<App text={'mount'} />, {
unstable_isConcurrent: true,
});
@@ -336,20 +336,20 @@ describe('StrictEffectsMode', () => {
assertLog(['componentDidMount']);
}
act(() => {
await act(async () => {
renderer.update(<App text={'update'} />);
});
assertLog(['componentDidUpdate']);
act(() => {
await act(async () => {
renderer.unmount();
});
assertLog(['componentWillUnmount']);
});
it('should not double invoke class lifecycles in legacy mode', () => {
it('should not double invoke class lifecycles in legacy mode', async () => {
class App extends React.PureComponent {
componentDidMount() {
Scheduler.log('componentDidMount');
@@ -368,14 +368,14 @@ describe('StrictEffectsMode', () => {
}
}
act(() => {
await act(async () => {
ReactTestRenderer.create(<App text={'mount'} />);
});
assertLog(['componentDidMount']);
});
it('double flushing passive effects only results in one double invoke', () => {
it('double flushing passive effects only results in one double invoke', async () => {
function App({text}) {
const [state, setState] = React.useState(0);
React.useEffect(() => {
@@ -395,7 +395,7 @@ describe('StrictEffectsMode', () => {
return text;
}
act(() => {
await act(async () => {
ReactTestRenderer.create(<App text={'mount'} />, {
unstable_isConcurrent: true,
});
@@ -430,7 +430,7 @@ describe('StrictEffectsMode', () => {
}
});
it('newly mounted components after initial mount get double invoked', () => {
it('newly mounted components after initial mount get double invoked', async () => {
let _setShowChild;
function Child() {
React.useEffect(() => {
@@ -460,7 +460,7 @@ describe('StrictEffectsMode', () => {
return showChild && <Child />;
}
act(() => {
await act(async () => {
ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});
});
@@ -477,7 +477,7 @@ describe('StrictEffectsMode', () => {
assertLog(['App useLayoutEffect mount', 'App useEffect mount']);
}
act(() => {
await act(async () => {
_setShowChild(true);
});
@@ -506,7 +506,7 @@ describe('StrictEffectsMode', () => {
}
});
it('classes and functions are double invoked together correctly', () => {
it('classes and functions are double invoked together correctly', async () => {
class ClassChild extends React.PureComponent {
componentDidMount() {
Scheduler.log('componentDidMount');
@@ -543,7 +543,7 @@ describe('StrictEffectsMode', () => {
}
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<App text={'mount'} />, {
unstable_isConcurrent: true,
});
@@ -569,7 +569,7 @@ describe('StrictEffectsMode', () => {
]);
}
act(() => {
await act(async () => {
renderer.update(<App text={'mount'} />);
});
@@ -580,7 +580,7 @@ describe('StrictEffectsMode', () => {
'useEffect mount',
]);
act(() => {
await act(async () => {
renderer.unmount();
});

View File

@@ -37,7 +37,7 @@ describe('StrictEffectsMode defaults', () => {
ReactFeatureFlags.createRootStrictEffectsByDefault = __DEV__;
});
it('should not double invoke effects in legacy mode', () => {
it('should not double invoke effects in legacy mode', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect mount');
@@ -52,14 +52,14 @@ describe('StrictEffectsMode defaults', () => {
return text;
}
act(() => {
await act(async () => {
ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);
});
assertLog(['useLayoutEffect mount', 'useEffect mount']);
});
it('should not double invoke class lifecycles in legacy mode', () => {
it('should not double invoke class lifecycles in legacy mode', async () => {
class App extends React.PureComponent {
componentDidMount() {
Scheduler.log('componentDidMount');
@@ -78,7 +78,7 @@ describe('StrictEffectsMode defaults', () => {
}
}
act(() => {
await act(async () => {
ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);
});
@@ -194,7 +194,7 @@ describe('StrictEffectsMode defaults', () => {
});
});
it('double invoking for effects for modern roots', () => {
it('double invoking for effects for modern roots', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect mount');
@@ -208,7 +208,7 @@ describe('StrictEffectsMode defaults', () => {
return text;
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -221,7 +221,7 @@ describe('StrictEffectsMode defaults', () => {
'useEffect mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(<App text={'update'} />);
});
@@ -232,14 +232,14 @@ describe('StrictEffectsMode defaults', () => {
'useEffect mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(null);
});
assertLog(['useLayoutEffect unmount', 'useEffect unmount']);
});
it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect One mount');
@@ -254,7 +254,7 @@ describe('StrictEffectsMode defaults', () => {
return text;
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -267,7 +267,7 @@ describe('StrictEffectsMode defaults', () => {
'useEffect Two mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(<App text={'update'} />);
});
@@ -278,14 +278,14 @@ describe('StrictEffectsMode defaults', () => {
'useEffect Two mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(null);
});
assertLog(['useEffect One unmount', 'useEffect Two unmount']);
});
it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {
it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
function App({text}) {
React.useLayoutEffect(() => {
Scheduler.log('useLayoutEffect One mount');
@@ -300,7 +300,7 @@ describe('StrictEffectsMode defaults', () => {
return text;
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -313,7 +313,7 @@ describe('StrictEffectsMode defaults', () => {
'useLayoutEffect Two mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(<App text={'update'} />);
});
@@ -324,14 +324,14 @@ describe('StrictEffectsMode defaults', () => {
'useLayoutEffect Two mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(null);
});
assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']);
});
it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {
it('useEffect and useLayoutEffect is called twice when there is no unmount', async () => {
function App({text}) {
React.useEffect(() => {
Scheduler.log('useEffect mount');
@@ -344,7 +344,7 @@ describe('StrictEffectsMode defaults', () => {
return text;
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -355,13 +355,13 @@ describe('StrictEffectsMode defaults', () => {
'useEffect mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(<App text={'update'} />);
});
assertLog(['useLayoutEffect mount', 'useEffect mount']);
act(() => {
await act(async () => {
ReactNoop.render(null);
});
@@ -369,7 +369,7 @@ describe('StrictEffectsMode defaults', () => {
});
//@gate useModernStrictMode
it('disconnects refs during double invoking', () => {
it('disconnects refs during double invoking', async () => {
const onRefMock = jest.fn();
function App({text}) {
return (
@@ -382,7 +382,7 @@ describe('StrictEffectsMode defaults', () => {
);
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -392,7 +392,7 @@ describe('StrictEffectsMode defaults', () => {
expect(onRefMock.mock.calls[2][0]).not.toBeNull();
});
it('passes the right context to class component lifecycles', () => {
it('passes the right context to class component lifecycles', async () => {
class App extends React.PureComponent {
test() {}
@@ -416,7 +416,7 @@ describe('StrictEffectsMode defaults', () => {
}
}
act(() => {
await act(async () => {
ReactNoop.render(<App />);
});
@@ -427,7 +427,7 @@ describe('StrictEffectsMode defaults', () => {
]);
});
it('double invoking works for class components', () => {
it('double invoking works for class components', async () => {
class App extends React.PureComponent {
componentDidMount() {
Scheduler.log('componentDidMount');
@@ -446,7 +446,7 @@ describe('StrictEffectsMode defaults', () => {
}
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -456,20 +456,20 @@ describe('StrictEffectsMode defaults', () => {
'componentDidMount',
]);
act(() => {
await act(async () => {
ReactNoop.render(<App text={'update'} />);
});
assertLog(['componentDidUpdate']);
act(() => {
await act(async () => {
ReactNoop.render(null);
});
assertLog(['componentWillUnmount']);
});
it('double flushing passive effects only results in one double invoke', () => {
it('double flushing passive effects only results in one double invoke', async () => {
function App({text}) {
const [state, setState] = React.useState(0);
React.useEffect(() => {
@@ -489,7 +489,7 @@ describe('StrictEffectsMode defaults', () => {
return text;
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -509,7 +509,7 @@ describe('StrictEffectsMode defaults', () => {
]);
});
it('newly mounted components after initial mount get double invoked', () => {
it('newly mounted components after initial mount get double invoked', async () => {
let _setShowChild;
function Child() {
React.useEffect(() => {
@@ -539,7 +539,7 @@ describe('StrictEffectsMode defaults', () => {
return showChild && <Child />;
}
act(() => {
await act(async () => {
ReactNoop.render(<App />);
});
@@ -552,7 +552,7 @@ describe('StrictEffectsMode defaults', () => {
'App useEffect mount',
]);
act(() => {
await act(async () => {
_setShowChild(true);
});
@@ -570,7 +570,7 @@ describe('StrictEffectsMode defaults', () => {
]);
});
it('classes and functions are double invoked together correctly', () => {
it('classes and functions are double invoked together correctly', async () => {
class ClassChild extends React.PureComponent {
componentDidMount() {
Scheduler.log('componentDidMount');
@@ -606,7 +606,7 @@ describe('StrictEffectsMode defaults', () => {
);
}
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -622,7 +622,7 @@ describe('StrictEffectsMode defaults', () => {
'useEffect mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(<App text={'mount'} />);
});
@@ -633,7 +633,7 @@ describe('StrictEffectsMode defaults', () => {
'useEffect mount',
]);
act(() => {
await act(async () => {
ReactNoop.render(null);
});

View File

@@ -604,7 +604,7 @@ describe('useEffectEvent', () => {
ReactNoop.render(<Counter value={1} />);
await waitForAll(['Effect value: 1', 'Event value: 1']);
act(() => ReactNoop.render(<Counter value={2} />));
await act(async () => ReactNoop.render(<Counter value={2} />));
assertLog(['Effect value: 2', 'Event value: 2']);
});
@@ -742,52 +742,58 @@ describe('useEffectEvent', () => {
return <Text text={`Welcome to the ${roomId} room!`} />;
}
act(() => ReactNoop.render(<ChatRoom roomId="general" theme="light" />));
assertLog(['Welcome to the general room!']);
await act(async () =>
ReactNoop.render(<ChatRoom roomId="general" theme="light" />),
);
await act(async () => jest.runAllTimers());
assertLog(['Welcome to the general room!', 'Connected! theme: light']);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the general room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
assertLog(['Connected! theme: light']);
// change roomId only
act(() => ReactNoop.render(<ChatRoom roomId="music" theme="light" />));
assertLog(['Welcome to the music room!']);
await act(async () =>
ReactNoop.render(<ChatRoom roomId="music" theme="light" />),
);
await act(async () => jest.runAllTimers());
assertLog([
'Welcome to the music room!',
// should trigger a reconnect
'Connected! theme: light',
]);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the music room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
// should trigger a reconnect
assertLog(['Connected! theme: light']);
// change theme only
act(() => ReactNoop.render(<ChatRoom roomId="music" theme="dark" />));
await act(async () =>
ReactNoop.render(<ChatRoom roomId="music" theme="dark" />),
);
await act(async () => jest.runAllTimers());
// should not trigger a reconnect
assertLog(['Welcome to the music room!']);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the music room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
// should not trigger a reconnect
await waitForAll([]);
// change roomId only
act(() => ReactNoop.render(<ChatRoom roomId="travel" theme="dark" />));
assertLog(['Welcome to the travel room!']);
await act(async () =>
ReactNoop.render(<ChatRoom roomId="travel" theme="dark" />),
);
await act(async () => jest.runAllTimers());
assertLog([
'Welcome to the travel room!',
// should trigger a reconnect
'Connected! theme: dark',
]);
expect(ReactNoop).toMatchRenderedOutput(
<span prop="Welcome to the travel room!" />,
);
jest.advanceTimersByTime(100);
Scheduler.unstable_advanceTime(100);
// should trigger a reconnect
assertLog(['Connected! theme: dark']);
});
// @gate enableUseEffectEventHook
it('integration: implements the docs logVisit example', () => {
it('integration: implements the docs logVisit example', async () => {
class AddToCartButton extends React.PureComponent {
addToCart = () => {
this.props.onClick();
@@ -835,7 +841,7 @@ describe('useEffectEvent', () => {
}
const button = React.createRef(null);
act(() =>
await act(async () =>
ReactNoop.render(
<AppShell>
<Page url="/shop/1" />
@@ -846,7 +852,7 @@ describe('useEffectEvent', () => {
act(button.current.addToCart);
assertLog(['Add to cart']);
act(() =>
await act(async () =>
ReactNoop.render(
<AppShell>
<Page url="/shop/2" />

View File

@@ -1666,7 +1666,7 @@ describe('useMutableSource', () => {
}
// Mount ComponentA with data version 1
act(() => {
await act(async () => {
ReactNoop.render(
<React.Profiler id="root" onRender={onRender}>
<ComponentA />
@@ -1703,7 +1703,7 @@ describe('useMutableSource', () => {
if (__DEV__) {
describe('dev warnings', () => {
// @gate enableUseMutableSource
it('should warn if the subscribe function does not return an unsubscribe function', () => {
it('should warn if the subscribe function does not return an unsubscribe function', async () => {
const source = createSource('one');
const mutableSource = createMutableSource(
source,
@@ -1712,8 +1712,8 @@ describe('useMutableSource', () => {
const brokenSubscribe = () => {};
expect(() => {
act(() => {
await expect(async () => {
await act(async () => {
ReactNoop.render(
<Component
label="only"

View File

@@ -142,7 +142,7 @@ describe('useMutableSourceHydration', () => {
}
// @gate enableUseMutableSource
it('should render and hydrate', () => {
it('should render and hydrate', async () => {
const source = createSource('one');
const mutableSource = createMutableSource(source, param => param.version);
@@ -165,7 +165,7 @@ describe('useMutableSourceHydration', () => {
assertLog(['only:one']);
expect(source.listenerCount).toBe(0);
act(() => {
await act(async () => {
ReactDOMClient.hydrateRoot(container, <TestComponent />, {
mutableSources: [mutableSource],
});
@@ -176,7 +176,7 @@ describe('useMutableSourceHydration', () => {
// @gate enableUseMutableSource
// @gate enableClientRenderFallbackOnTextMismatch
it('should detect a tear before hydrating a component', () => {
it('should detect a tear before hydrating a component', async () => {
const source = createSource('one');
const mutableSource = createMutableSource(source, param => param.version);
@@ -199,8 +199,8 @@ describe('useMutableSourceHydration', () => {
assertLog(['only:one']);
expect(source.listenerCount).toBe(0);
expect(() => {
act(() => {
await expect(async () => {
await act(async () => {
ReactDOMClient.hydrateRoot(container, <TestComponent />, {
mutableSources: [mutableSource],
onRecoverableError(error) {

View File

@@ -50,7 +50,7 @@ describe('useRef', () => {
return <span prop={props.text} />;
}
it('creates a ref object initialized with the provided value', () => {
it('creates a ref object initialized with the provided value', async () => {
jest.useFakeTimers();
function useDebouncedCallback(callback, ms, inputs) {
@@ -82,7 +82,7 @@ describe('useRef', () => {
return null;
}
act(() => {
await act(async () => {
ReactNoop.render(<App />);
});
assertLog([]);
@@ -134,7 +134,7 @@ describe('useRef', () => {
});
if (__DEV__) {
it('should never warn when attaching to children', () => {
it('should never warn when attaching to children', async () => {
class Component extends React.Component {
render() {
return null;
@@ -152,16 +152,16 @@ describe('useRef', () => {
);
}
act(() => {
await act(async () => {
ReactNoop.render(<Example phase="mount" />);
});
act(() => {
await act(async () => {
ReactNoop.render(<Example phase="update" />);
});
});
// @gate enableUseRefAccessWarning
it('should warn about reads during render', () => {
it('should warn about reads during render', async () => {
function Example() {
const ref = useRef(123);
let value;
@@ -173,12 +173,12 @@ describe('useRef', () => {
return value;
}
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
});
it('should not warn about lazy init during render', () => {
it('should not warn about lazy init during render', async () => {
function Example() {
const ref1 = useRef(null);
const ref2 = useRef(undefined);
@@ -192,17 +192,17 @@ describe('useRef', () => {
return null;
}
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
// Should not warn after an update either.
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
});
it('should not warn about lazy init outside of render', () => {
it('should not warn about lazy init outside of render', async () => {
function Example() {
// eslint-disable-next-line no-unused-vars
const [didMount, setDidMount] = useState(false);
@@ -216,13 +216,13 @@ describe('useRef', () => {
return null;
}
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
});
// @gate enableUseRefAccessWarning
it('should warn about unconditional lazy init during render', () => {
it('should warn about unconditional lazy init during render', async () => {
function Example() {
const ref1 = useRef(null);
const ref2 = useRef(undefined);
@@ -251,19 +251,19 @@ describe('useRef', () => {
}
let shouldExpectWarning = true;
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
// Should not warn again on update.
shouldExpectWarning = false;
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
});
// @gate enableUseRefAccessWarning
it('should warn about reads to ref after lazy init pattern', () => {
it('should warn about reads to ref after lazy init pattern', async () => {
function Example() {
const ref1 = useRef(null);
const ref2 = useRef(undefined);
@@ -291,13 +291,13 @@ describe('useRef', () => {
return value;
}
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
});
// @gate enableUseRefAccessWarning
it('should warn about writes to ref after lazy init pattern', () => {
it('should warn about writes to ref after lazy init pattern', async () => {
function Example() {
const ref1 = useRef(null);
const ref2 = useRef(undefined);
@@ -323,12 +323,12 @@ describe('useRef', () => {
return null;
}
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
});
it('should not warn about reads or writes within effect', () => {
it('should not warn about reads or writes within effect', async () => {
function Example() {
const ref = useRef(123);
useLayoutEffect(() => {
@@ -344,21 +344,21 @@ describe('useRef', () => {
return null;
}
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});
ReactNoop.flushPassiveEffects();
});
it('should not warn about reads or writes outside of render phase (e.g. event handler)', () => {
it('should not warn about reads or writes outside of render phase (e.g. event handler)', async () => {
let ref;
function Example() {
ref = useRef(123);
return null;
}
act(() => {
await act(async () => {
ReactNoop.render(<Example />);
});

View File

@@ -195,19 +195,19 @@ describe('useSyncExternalStore', () => {
}
const root = ReactNoop.createRoot();
act(() => {
await act(async () => {
// Start a render that reads from the store and yields value
root.render(<App />);
});
assertLog(['value:initial']);
await act(() => {
await act(async () => {
store.set('value:changed');
});
assertLog(['value:changed']);
// If cached value was updated, we expect a re-render
await act(() => {
await act(async () => {
store.set('value:initial');
});
assertLog(['value:initial']);

View File

@@ -17,6 +17,7 @@ let ReactDOMClient;
let ReactFreshRuntime;
let Scheduler;
let act;
let internalAct;
let createReactClass;
let waitFor;
let assertLog;
@@ -33,7 +34,8 @@ describe('ReactFresh', () => {
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
Scheduler = require('scheduler');
act = require('jest-react').act;
act = require('react-dom/test-utils').act;
internalAct = require('jest-react').act;
const InternalTestUtils = require('internal-test-utils');
waitFor = InternalTestUtils.waitFor;
@@ -2480,7 +2482,7 @@ describe('ReactFresh', () => {
expect(el.firstChild.textContent).toBe('0');
expect(el.firstChild.style.color).toBe('red');
await act(async () => {
await internalAct(async () => {
el.firstChild.dispatchEvent(
new MouseEvent('click', {
bubbles: true,

View File

@@ -31,7 +31,7 @@ describe('ReactFreshIntegration', () => {
ReactFreshRuntime = require('react-refresh/runtime');
ReactFreshRuntime.injectIntoGlobalHook(global);
ReactDOM = require('react-dom');
act = require('jest-react').act;
act = require('react-dom/test-utils').act;
container = document.createElement('div');
document.body.appendChild(container);
exportsObj = undefined;

View File

@@ -85,7 +85,7 @@ describe('ReactFlightDOMRelay', () => {
});
});
it('can render a Client Component using a module reference and render there', () => {
it('can render a Client Component using a module reference and render there', async () => {
function UserClient(props) {
return (
<span>
@@ -110,7 +110,7 @@ describe('ReactFlightDOMRelay', () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
act(() => {
await act(async () => {
root.render(modelClient.greeting);
});

View File

@@ -81,7 +81,7 @@ describe('useSubscription', () => {
const observable = createBehaviorSubject();
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(
<Subscription source={observable} />,
{unstable_isConcurrent: true},
@@ -90,14 +90,14 @@ describe('useSubscription', () => {
assertLog(['default']);
// Updates while subscribed should re-render the child component
act(() => observable.next(123));
await act(async () => observable.next(123));
assertLog([123]);
act(() => observable.next('abc'));
await act(async () => observable.next('abc'));
assertLog(['abc']);
// Unmounting the subscriber should remove listeners
act(() => renderer.update(<div />));
act(() => observable.next(456));
await act(async () => renderer.update(<div />));
await act(async () => observable.next(456));
await waitForAll([]);
});
@@ -133,21 +133,23 @@ describe('useSubscription', () => {
let observable = createReplaySubject('initial');
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(
<Subscription source={observable} />,
{unstable_isConcurrent: true},
);
});
assertLog(['initial']);
act(() => observable.next('updated'));
await act(async () => observable.next('updated'));
assertLog(['updated']);
await waitForAll([]);
// Unsetting the subscriber prop should reset subscribed values
observable = createReplaySubject(undefined);
act(() => renderer.update(<Subscription source={observable} />));
await act(async () =>
renderer.update(<Subscription source={observable} />),
);
assertLog(['default']);
});
@@ -182,7 +184,7 @@ describe('useSubscription', () => {
expect(subscriptions).toHaveLength(0);
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(
<Subscription source={observableA} />,
{unstable_isConcurrent: true},
@@ -195,18 +197,20 @@ describe('useSubscription', () => {
expect(subscriptions[0]).toBe(observableA);
// Unsetting the subscriber prop should reset subscribed values
act(() => renderer.update(<Subscription source={observableB} />));
await act(async () =>
renderer.update(<Subscription source={observableB} />),
);
assertLog(['b-0']);
expect(subscriptions).toHaveLength(2);
expect(subscriptions[1]).toBe(observableB);
// Updates to the old subscribable should not re-render the child component
act(() => observableA.next('a-1'));
await act(async () => observableA.next('a-1'));
await waitForAll([]);
// Updates to the bew subscribable should re-render the child component
act(() => observableB.next('b-1'));
await act(async () => observableB.next('b-1'));
assertLog(['b-1']);
expect(subscriptions).toHaveLength(2);
@@ -241,7 +245,7 @@ describe('useSubscription', () => {
expect(subscriptions).toHaveLength(0);
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(
<Subscription source={observableA} />,
{unstable_isConcurrent: true},
@@ -254,17 +258,19 @@ describe('useSubscription', () => {
expect(subscriptions[0]).toBe(observableA);
// Unsetting the subscriber prop should reset subscribed values
act(() => renderer.update(<Subscription source={observableB} />));
await act(async () =>
renderer.update(<Subscription source={observableB} />),
);
assertLog(['b-0']);
expect(subscriptions).toHaveLength(2);
expect(subscriptions[1]).toBe(observableB);
// Updates to the old subscribable should not re-render the child component
act(() => observableA.next('a-1'));
await act(async () => observableA.next('a-1'));
await waitForAll([]);
// Updates to the bew subscribable should re-render the child component
act(() => observableB.next('b-1'));
await act(async () => observableB.next('b-1'));
assertLog(['b-1']);
expect(subscriptions).toHaveLength(2);
@@ -329,7 +335,7 @@ describe('useSubscription', () => {
const observableB = createBehaviorSubject('b-0');
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<Parent observed={observableA} />, {
unstable_isConcurrent: true,
});
@@ -353,7 +359,7 @@ describe('useSubscription', () => {
});
// Update again
act(() => renderer.update(<Parent observed={observableA} />));
await act(async () => renderer.update(<Parent observed={observableA} />));
// Flush everything and ensure that the correct subscribable is used
// We expect the last emitted update to be rendered (because of the commit phase value check)
@@ -432,7 +438,7 @@ describe('useSubscription', () => {
const observableB = createBehaviorSubject('b-0');
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(<Parent observed={observableA} />, {
unstable_isConcurrent: true,
});
@@ -474,12 +480,12 @@ describe('useSubscription', () => {
// Updates from the new subscribable should be ignored.
log.splice(0);
act(() => observableB.next('b-1'));
await act(async () => observableB.next('b-1'));
await waitForAll([]);
expect(log).toEqual([]);
});
it('should guard against updates that happen after unmounting', () => {
it('should guard against updates that happen after unmounting', async () => {
function Child({value = 'default'}) {
Scheduler.log(value);
return null;
@@ -529,7 +535,7 @@ describe('useSubscription', () => {
});
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(
<Subscription source={eventHandler} />,
{unstable_isConcurrent: true},
@@ -563,7 +569,7 @@ describe('useSubscription', () => {
}
let renderer;
act(() => {
await act(async () => {
renderer = ReactTestRenderer.create(
<Subscription subscription={subscription1} />,
{unstable_isConcurrent: true},
@@ -571,7 +577,9 @@ describe('useSubscription', () => {
});
await waitForAll([]);
act(() => renderer.update(<Subscription subscription={subscription2} />));
await act(async () =>
renderer.update(<Subscription subscription={subscription2} />),
);
await waitForAll([]);
});