Skip to content

Commit ba31ad4

Browse files
authored
feat(StrictMode): Double-invoke render for every component (#18430)
* feat(StrictMode): Double-invoke render for every component * fix: Mark ReactTestRendererAsync as internal
1 parent 689d275 commit ba31ad4

12 files changed

+76
-73
lines changed

packages/react-art/src/__tests__/ReactART-test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ describe('ReactART', () => {
391391
</CurrentRendererContext.Provider>,
392392
);
393393

394-
expect(Scheduler).toFlushAndYieldThrough(['A']);
394+
expect(Scheduler).toFlushAndYieldThrough(__DEV__ ? ['A', 'A'] : ['A']);
395395

396396
ReactDOM.render(
397397
<Surface>
@@ -406,7 +406,9 @@ describe('ReactART', () => {
406406
expect(ops).toEqual([null, 'ART']);
407407

408408
ops = [];
409-
expect(Scheduler).toFlushAndYield(['B', 'C']);
409+
expect(Scheduler).toFlushAndYield(
410+
__DEV__ ? ['B', 'B', 'C', 'C'] : ['B', 'C'],
411+
);
410412

411413
expect(ops).toEqual(['Test']);
412414
});

packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,9 @@ describe('ReactDOMFiberAsync', () => {
639639
expect(container.textContent).toEqual('');
640640

641641
// Everything should render immediately in the next event
642-
expect(Scheduler).toFlushExpired(['A', 'B', 'C']);
642+
expect(Scheduler).toFlushExpired(
643+
__DEV__ ? ['A', 'A', 'B', 'B', 'C', 'C'] : ['A', 'B', 'C'],
644+
);
643645
expect(container.textContent).toEqual('ABC');
644646
});
645647
});

packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,9 @@ function runActTests(label, render, unmount, rerender) {
607607
},
608608
);
609609

610-
expect(Component).toHaveBeenCalledTimes(4);
610+
expect(Component).toHaveBeenCalledTimes(
611+
label === 'legacy mode' ? 4 : 8,
612+
);
611613
unmount(secondContainer);
612614
});
613615
}

packages/react-dom/src/__tests__/ReactUpdates-test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,7 @@ describe('ReactUpdates', () => {
13331333
'Foo',
13341334
'Foo',
13351335
'Baz',
1336+
'Baz',
13361337
'Foo#effect',
13371338
]);
13381339
} else {

packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ describe('DOMEventResponderSystem', () => {
813813

814814
let root = ReactDOM.createRoot(container);
815815
root.render(<Test counter={0} />);
816-
expect(Scheduler).toFlushAndYield(['Test']);
816+
expect(Scheduler).toFlushAndYield(__DEV__ ? ['Test', 'Test'] : ['Test']);
817817

818818
// Click the button
819819
dispatchClickEvent(ref.current);
@@ -825,7 +825,9 @@ describe('DOMEventResponderSystem', () => {
825825
// Increase counter
826826
root.render(<Test counter={1} />);
827827
// Yield before committing
828-
expect(Scheduler).toFlushAndYieldThrough(['Test']);
828+
expect(Scheduler).toFlushAndYieldThrough(
829+
__DEV__ ? ['Test', 'Test'] : ['Test'],
830+
);
829831

830832
// Click the button again
831833
dispatchClickEvent(ref.current);

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -321,17 +321,14 @@ function updateForwardRef(
321321
debugRenderPhaseSideEffectsForStrictMode &&
322322
workInProgress.mode & StrictMode
323323
) {
324-
// Only double-render components with Hooks
325-
if (workInProgress.memoizedState !== null) {
326-
nextChildren = renderWithHooks(
327-
current,
328-
workInProgress,
329-
render,
330-
nextProps,
331-
ref,
332-
renderExpirationTime,
333-
);
334-
}
324+
nextChildren = renderWithHooks(
325+
current,
326+
workInProgress,
327+
render,
328+
nextProps,
329+
ref,
330+
renderExpirationTime,
331+
);
335332
}
336333
setIsRendering(false);
337334
} else {
@@ -662,17 +659,14 @@ function updateFunctionComponent(
662659
debugRenderPhaseSideEffectsForStrictMode &&
663660
workInProgress.mode & StrictMode
664661
) {
665-
// Only double-render components with Hooks
666-
if (workInProgress.memoizedState !== null) {
667-
nextChildren = renderWithHooks(
668-
current,
669-
workInProgress,
670-
Component,
671-
nextProps,
672-
context,
673-
renderExpirationTime,
674-
);
675-
}
662+
nextChildren = renderWithHooks(
663+
current,
664+
workInProgress,
665+
Component,
666+
nextProps,
667+
context,
668+
renderExpirationTime,
669+
);
676670
}
677671
setIsRendering(false);
678672
} else {
@@ -738,17 +732,14 @@ function updateBlock<Props, Data>(
738732
debugRenderPhaseSideEffectsForStrictMode &&
739733
workInProgress.mode & StrictMode
740734
) {
741-
// Only double-render components with Hooks
742-
if (workInProgress.memoizedState !== null) {
743-
nextChildren = renderWithHooks(
744-
current,
745-
workInProgress,
746-
render,
747-
nextProps,
748-
data,
749-
renderExpirationTime,
750-
);
751-
}
735+
nextChildren = renderWithHooks(
736+
current,
737+
workInProgress,
738+
render,
739+
nextProps,
740+
data,
741+
renderExpirationTime,
742+
);
752743
}
753744
setIsRendering(false);
754745
} else {
@@ -1471,17 +1462,14 @@ function mountIndeterminateComponent(
14711462
debugRenderPhaseSideEffectsForStrictMode &&
14721463
workInProgress.mode & StrictMode
14731464
) {
1474-
// Only double-render components with Hooks
1475-
if (workInProgress.memoizedState !== null) {
1476-
value = renderWithHooks(
1477-
null,
1478-
workInProgress,
1479-
Component,
1480-
props,
1481-
context,
1482-
renderExpirationTime,
1483-
);
1484-
}
1465+
value = renderWithHooks(
1466+
null,
1467+
workInProgress,
1468+
Component,
1469+
props,
1470+
context,
1471+
renderExpirationTime,
1472+
);
14851473
}
14861474
}
14871475
reconcileChildren(null, workInProgress, value, renderExpirationTime);

packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ describe('ErrorBoundaryReconciliation', () => {
1212
jest.resetModules();
1313

1414
ReactFeatureFlags = require('shared/ReactFeatureFlags');
15+
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
1516
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
1617
ReactTestRenderer = require('react-test-renderer');
1718
React = require('react');

packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,14 +1300,14 @@ describe('ReactHooks', () => {
13001300
<NoHooks />
13011301
</StrictMode>,
13021302
);
1303-
expect(renderCount).toBe(1);
1303+
expect(renderCount).toBe(__DEV__ ? 2 : 1);
13041304
renderCount = 0;
13051305
renderer.update(
13061306
<StrictMode>
13071307
<NoHooks />
13081308
</StrictMode>,
13091309
);
1310-
expect(renderCount).toBe(1);
1310+
expect(renderCount).toBe(__DEV__ ? 2 : 1);
13111311

13121312
renderCount = 0;
13131313
renderer.update(<FwdRef />);
@@ -1321,14 +1321,14 @@ describe('ReactHooks', () => {
13211321
<FwdRef />
13221322
</StrictMode>,
13231323
);
1324-
expect(renderCount).toBe(1);
1324+
expect(renderCount).toBe(__DEV__ ? 2 : 1);
13251325
renderCount = 0;
13261326
renderer.update(
13271327
<StrictMode>
13281328
<FwdRef />
13291329
</StrictMode>,
13301330
);
1331-
expect(renderCount).toBe(1);
1331+
expect(renderCount).toBe(__DEV__ ? 2 : 1);
13321332

13331333
renderCount = 0;
13341334
renderer.update(<Memo arg={1} />);
@@ -1342,14 +1342,14 @@ describe('ReactHooks', () => {
13421342
<Memo arg={1} />
13431343
</StrictMode>,
13441344
);
1345-
expect(renderCount).toBe(1);
1345+
expect(renderCount).toBe(__DEV__ ? 2 : 1);
13461346
renderCount = 0;
13471347
renderer.update(
13481348
<StrictMode>
13491349
<Memo arg={2} />
13501350
</StrictMode>,
13511351
);
1352-
expect(renderCount).toBe(1);
1352+
expect(renderCount).toBe(__DEV__ ? 2 : 1);
13531353

13541354
renderCount = 0;
13551355
expect(() => renderer.update(<Factory />)).toErrorDev(

packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,9 @@ describe('ReactNewContext', () => {
11031103

11041104
// Render the provider again using a different renderer
11051105
ReactNoop.render(<App value={1} />);
1106-
expect(Scheduler).toFlushAndYield(['Foo', 'Foo']);
1106+
expect(Scheduler).toFlushAndYield(
1107+
__DEV__ ? ['Foo', 'Foo', 'Foo', 'Foo'] : ['Foo', 'Foo'],
1108+
);
11071109

11081110
if (__DEV__) {
11091111
expect(console.error.calls.argsFor(0)[0]).toContain(

packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js renamed to packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.internal.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
'use strict';
1212

1313
let React;
14+
let ReactFeatureFlags;
1415
let ReactTestRenderer;
1516
let Scheduler;
1617

1718
describe('ReactTestRendererAsync', () => {
1819
beforeEach(() => {
1920
jest.resetModules();
21+
ReactFeatureFlags = require('shared/ReactFeatureFlags');
22+
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
2023
React = require('react');
2124
ReactTestRenderer = require('react-test-renderer');
2225
Scheduler = require('scheduler');

0 commit comments

Comments
 (0)