@samitouri / QOS-React-2 / commits / 0f3838a01b

Remove `debugRenderPhaseSideEffects` flag (#17270)

There are two similar flags, `debugRenderPhaseSideEffects` and `debugRenderPhaseSideEffectsForStrictMode`. The strict mode one is the only one that is actually used. I think originally the theory is that we would one day turn it on for all components, even outside strict mode. But what we'll do instead is migrate everyone to strict mode. The only place `debugRenderPhaseSideEffects` was being used was in an internal test file. I rewrote those tests to use public APIs.

Andrew Clark committed Nov 4, 2019 at 14:07 UTC 0f3838a01b0fda0ac5fd054c6be13166697a113c
15 files changed +839 -882
packages/react-reconciler/src/ReactFiberBeginWork.js
+8 -13
@@ -56,7 +56,6 @@ import {
56 } from 'shared/ReactSideEffectTags';
57 import ReactSharedInternals from 'shared/ReactSharedInternals';
58 import {
59 - debugRenderPhaseSideEffects,
59 debugRenderPhaseSideEffectsForStrictMode,
60 disableLegacyContext,
61 enableProfilerTimer,
@@ -319,9 +318,8 @@ function updateForwardRef(
318 renderExpirationTime,
319 );
320 if (
322 - debugRenderPhaseSideEffects ||
323 - (debugRenderPhaseSideEffectsForStrictMode &&
324 - workInProgress.mode & StrictMode)
321 + debugRenderPhaseSideEffectsForStrictMode &&
322 + workInProgress.mode & StrictMode
323 ) {
324 // Only double-render components with Hooks
325 if (workInProgress.memoizedState !== null) {
@@ -642,9 +640,8 @@ function updateFunctionComponent(
640 renderExpirationTime,
641 );
642 if (
645 - debugRenderPhaseSideEffects ||
646 - (debugRenderPhaseSideEffectsForStrictMode &&
647 - workInProgress.mode & StrictMode)
643 + debugRenderPhaseSideEffectsForStrictMode &&
644 + workInProgress.mode & StrictMode
645 ) {
646 // Only double-render components with Hooks
647 if (workInProgress.memoizedState !== null) {
@@ -843,9 +840,8 @@ function finishClassComponent(
840 setCurrentPhase('render');
841 nextChildren = instance.render();
842 if (
846 - debugRenderPhaseSideEffects ||
847 - (debugRenderPhaseSideEffectsForStrictMode &&
848 - workInProgress.mode & StrictMode)
843 + debugRenderPhaseSideEffectsForStrictMode &&
844 + workInProgress.mode & StrictMode
845 ) {
846 instance.render();
847 }
@@ -1376,9 +1372,8 @@ function mountIndeterminateComponent(
1372 }
1373
1374 if (
1379 - debugRenderPhaseSideEffects ||
1380 - (debugRenderPhaseSideEffectsForStrictMode &&
1381 - workInProgress.mode & StrictMode)
1375 + debugRenderPhaseSideEffectsForStrictMode &&
1376 + workInProgress.mode & StrictMode
1377 ) {
1378 // Only double-render components with Hooks
1379 if (workInProgress.memoizedState !== null) {
packages/react-reconciler/src/ReactFiberClassComponent.js
+4 -7
@@ -13,7 +13,6 @@ import type {ExpirationTime} from './ReactFiberExpirationTime';
13 import React from 'react';
14 import {Update, Snapshot} from 'shared/ReactSideEffectTags';
15 import {
16 - debugRenderPhaseSideEffects,
16 debugRenderPhaseSideEffectsForStrictMode,
17 disableLegacyContext,
18 warnAboutDeprecatedLifecycles,
@@ -150,9 +149,8 @@ export function applyDerivedStateFromProps(
149
150 if (__DEV__) {
151 if (
153 - debugRenderPhaseSideEffects ||
154 - (debugRenderPhaseSideEffectsForStrictMode &&
155 - workInProgress.mode & StrictMode)
152 + debugRenderPhaseSideEffectsForStrictMode &&
153 + workInProgress.mode & StrictMode
154 ) {
155 // Invoke the function an extra time to help detect side-effects.
156 getDerivedStateFromProps(nextProps, prevState);
@@ -605,9 +603,8 @@ function constructClassInstance(
603 // Instantiate twice to help detect side-effects.
604 if (__DEV__) {
605 if (
608 - debugRenderPhaseSideEffects ||
609 - (debugRenderPhaseSideEffectsForStrictMode &&
610 - workInProgress.mode & StrictMode)
606 + debugRenderPhaseSideEffectsForStrictMode &&
607 + workInProgress.mode & StrictMode
608 ) {
609 new ctor(props, context); // eslint-disable-line no-new
610 }
packages/react-reconciler/src/ReactUpdateQueue.js
+5 -10
@@ -97,10 +97,7 @@ import {
97 import {Callback, ShouldCapture, DidCapture} from 'shared/ReactSideEffectTags';
98 import {ClassComponent} from 'shared/ReactWorkTags';
99
100 -import {
101 - debugRenderPhaseSideEffects,
102 - debugRenderPhaseSideEffectsForStrictMode,
103 -} from 'shared/ReactFeatureFlags';
100 +import {debugRenderPhaseSideEffectsForStrictMode} from 'shared/ReactFeatureFlags';
101
102 import {StrictMode} from './ReactTypeOfMode';
103 import {
@@ -373,9 +370,8 @@ function getStateFromUpdate<State>(
370 if (__DEV__) {
371 enterDisallowedContextReadInDEV();
372 if (
376 - debugRenderPhaseSideEffects ||
377 - (debugRenderPhaseSideEffectsForStrictMode &&
378 - workInProgress.mode & StrictMode)
373 + debugRenderPhaseSideEffectsForStrictMode &&
374 + workInProgress.mode & StrictMode
375 ) {
376 payload.call(instance, prevState, nextProps);
377 }
@@ -402,9 +398,8 @@ function getStateFromUpdate<State>(
398 if (__DEV__) {
399 enterDisallowedContextReadInDEV();
400 if (
405 - debugRenderPhaseSideEffects ||
406 - (debugRenderPhaseSideEffectsForStrictMode &&
407 - workInProgress.mode & StrictMode)
401 + debugRenderPhaseSideEffectsForStrictMode &&
402 + workInProgress.mode & StrictMode
403 ) {
404 payload.call(instance, prevState, nextProps);
405 }
packages/react/src/__tests__/ReactDOMTracing-test.internal.js
-1
@@ -25,7 +25,6 @@ let onWorkStopped;
25
26 function loadModules() {
27 ReactFeatureFlags = require('shared/ReactFeatureFlags');
28 - ReactFeatureFlags.debugRenderPhaseSideEffects = false;
28 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
29 ReactFeatureFlags.enableProfilerTimer = true;
30 ReactFeatureFlags.enableSchedulerTracing = true;
packages/react/src/__tests__/ReactProfiler-test.internal.js
-1
@@ -30,7 +30,6 @@ function loadModules({
30 useNoopRenderer = false,
31 } = {}) {
32 ReactFeatureFlags = require('shared/ReactFeatureFlags');
33 - ReactFeatureFlags.debugRenderPhaseSideEffects = false;
33 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
34 ReactFeatureFlags.enableProfilerTimer = enableProfilerTimer;
35 ReactFeatureFlags.enableSchedulerTracing = enableSchedulerTracing;
packages/react/src/__tests__/ReactProfilerDOM-test.internal.js
-1
@@ -17,7 +17,6 @@ let Scheduler;
17
18 function loadModules() {
19 ReactFeatureFlags = require('shared/ReactFeatureFlags');
20 - ReactFeatureFlags.debugRenderPhaseSideEffects = false;
20 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
21 ReactFeatureFlags.enableProfilerTimer = true;
22 ReactFeatureFlags.enableSchedulerTracing = true;
packages/react/src/__tests__/ReactStrictMode-test.internal.js deleted
-833
@@ -1,833 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -let React;
13 -let Scheduler;
14 -let ReactFeatureFlags;
15 -let ReactTestRenderer;
16 -let PropTypes;
17 -
18 -describe('ReactStrictMode', () => {
19 - describe('debugRenderPhaseSideEffects', () => {
20 - beforeEach(() => {
21 - jest.resetModules();
22 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
23 - ReactFeatureFlags.debugRenderPhaseSideEffects = true;
24 - React = require('react');
25 - Scheduler = require('scheduler');
26 - ReactTestRenderer = require('react-test-renderer');
27 - });
28 -
29 - it('should invoke precommit lifecycle methods twice', () => {
30 - let log = [];
31 - let shouldComponentUpdate = false;
32 - class ClassComponent extends React.Component {
33 - state = {};
34 - static getDerivedStateFromProps() {
35 - log.push('getDerivedStateFromProps');
36 - return null;
37 - }
38 - constructor(props) {
39 - super(props);
40 - log.push('constructor');
41 - }
42 - componentDidMount() {
43 - log.push('componentDidMount');
44 - }
45 - componentDidUpdate() {
46 - log.push('componentDidUpdate');
47 - }
48 - componentWillUnmount() {
49 - log.push('componentWillUnmount');
50 - }
51 - shouldComponentUpdate() {
52 - log.push('shouldComponentUpdate');
53 - return shouldComponentUpdate;
54 - }
55 - render() {
56 - log.push('render');
57 - return null;
58 - }
59 - }
60 -
61 - const component = ReactTestRenderer.create(<ClassComponent />);
62 -
63 - if (__DEV__) {
64 - expect(log).toEqual([
65 - 'constructor',
66 - 'constructor',
67 - 'getDerivedStateFromProps',
68 - 'getDerivedStateFromProps',
69 - 'render',
70 - 'render',
71 - 'componentDidMount',
72 - ]);
73 - } else {
74 - expect(log).toEqual([
75 - 'constructor',
76 - 'getDerivedStateFromProps',
77 - 'render',
78 - 'componentDidMount',
79 - ]);
80 - }
81 -
82 - log = [];
83 - shouldComponentUpdate = true;
84 -
85 - component.update(<ClassComponent />);
86 - if (__DEV__) {
87 - expect(log).toEqual([
88 - 'getDerivedStateFromProps',
89 - 'getDerivedStateFromProps',
90 - 'shouldComponentUpdate',
91 - 'render',
92 - 'render',
93 - 'componentDidUpdate',
94 - ]);
95 - } else {
96 - expect(log).toEqual([
97 - 'getDerivedStateFromProps',
98 - 'shouldComponentUpdate',
99 - 'render',
100 - 'componentDidUpdate',
101 - ]);
102 - }
103 -
104 - log = [];
105 - shouldComponentUpdate = false;
106 -
107 - component.update(<ClassComponent />);
108 -
109 - if (__DEV__) {
110 - expect(log).toEqual([
111 - 'getDerivedStateFromProps',
112 - 'getDerivedStateFromProps',
113 - 'shouldComponentUpdate',
114 - ]);
115 - } else {
116 - expect(log).toEqual([
117 - 'getDerivedStateFromProps',
118 - 'shouldComponentUpdate',
119 - ]);
120 - }
121 - });
122 -
123 - it('should invoke setState callbacks twice', () => {
124 - class ClassComponent extends React.Component {
125 - state = {
126 - count: 1,
127 - };
128 - render() {
129 - return null;
130 - }
131 - }
132 -
133 - let setStateCount = 0;
134 -
135 - const rendered = ReactTestRenderer.create(<ClassComponent />);
136 - const instance = rendered.getInstance();
137 - instance.setState(state => {
138 - setStateCount++;
139 - return {
140 - count: state.count + 1,
141 - };
142 - });
143 -
144 - // Callback should be invoked twice in DEV
145 - expect(setStateCount).toBe(__DEV__ ? 2 : 1);
146 - // But each time `state` should be the previous value
147 - expect(instance.state.count).toBe(2);
148 - });
149 - });
150 -
151 - [true, false].forEach(debugRenderPhaseSideEffectsForStrictMode => {
152 - describe(`StrictMode (${debugRenderPhaseSideEffectsForStrictMode})`, () => {
153 - beforeEach(() => {
154 - jest.resetModules();
155 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
156 - ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = debugRenderPhaseSideEffectsForStrictMode;
157 - React = require('react');
158 - Scheduler = require('scheduler');
159 - ReactTestRenderer = require('react-test-renderer');
160 - });
161 -
162 - it('should invoke precommit lifecycle methods twice in DEV', () => {
163 - const {StrictMode} = React;
164 -
165 - let log = [];
166 - let shouldComponentUpdate = false;
167 -
168 - function Root() {
169 - return (
170 - <StrictMode>
171 - <ClassComponent />
172 - </StrictMode>
173 - );
174 - }
175 -
176 - class ClassComponent extends React.Component {
177 - state = {};
178 - static getDerivedStateFromProps() {
179 - log.push('getDerivedStateFromProps');
180 - return null;
181 - }
182 - constructor(props) {
183 - super(props);
184 - log.push('constructor');
185 - }
186 - componentDidMount() {
187 - log.push('componentDidMount');
188 - }
189 - componentDidUpdate() {
190 - log.push('componentDidUpdate');
191 - }
192 - componentWillUnmount() {
193 - log.push('componentWillUnmount');
194 - }
195 - shouldComponentUpdate() {
196 - log.push('shouldComponentUpdate');
197 - return shouldComponentUpdate;
198 - }
199 - render() {
200 - log.push('render');
201 - return null;
202 - }
203 - }
204 -
205 - const component = ReactTestRenderer.create(<Root />);
206 -
207 - if (__DEV__ && debugRenderPhaseSideEffectsForStrictMode) {
208 - expect(log).toEqual([
209 - 'constructor',
210 - 'constructor',
211 - 'getDerivedStateFromProps',
212 - 'getDerivedStateFromProps',
213 - 'render',
214 - 'render',
215 - 'componentDidMount',
216 - ]);
217 - } else {
218 - expect(log).toEqual([
219 - 'constructor',
220 - 'getDerivedStateFromProps',
221 - 'render',
222 - 'componentDidMount',
223 - ]);
224 - }
225 -
226 - log = [];
227 - shouldComponentUpdate = true;
228 -
229 - component.update(<Root />);
230 - if (__DEV__ && debugRenderPhaseSideEffectsForStrictMode) {
231 - expect(log).toEqual([
232 - 'getDerivedStateFromProps',
233 - 'getDerivedStateFromProps',
234 - 'shouldComponentUpdate',
235 - 'render',
236 - 'render',
237 - 'componentDidUpdate',
238 - ]);
239 - } else {
240 - expect(log).toEqual([
241 - 'getDerivedStateFromProps',
242 - 'shouldComponentUpdate',
243 - 'render',
244 - 'componentDidUpdate',
245 - ]);
246 - }
247 -
248 - log = [];
249 - shouldComponentUpdate = false;
250 -
251 - component.update(<Root />);
252 - if (__DEV__ && debugRenderPhaseSideEffectsForStrictMode) {
253 - expect(log).toEqual([
254 - 'getDerivedStateFromProps',
255 - 'getDerivedStateFromProps',
256 - 'shouldComponentUpdate',
257 - ]);
258 - } else {
259 - expect(log).toEqual([
260 - 'getDerivedStateFromProps',
261 - 'shouldComponentUpdate',
262 - ]);
263 - }
264 - });
265 -
266 - it('should invoke setState callbacks twice in DEV', () => {
267 - const {StrictMode} = React;
268 -
269 - let instance;
270 - class ClassComponent extends React.Component {
271 - state = {
272 - count: 1,
273 - };
274 - render() {
275 - instance = this;
276 - return null;
277 - }
278 - }
279 -
280 - let setStateCount = 0;
281 -
282 - ReactTestRenderer.create(
283 - <StrictMode>
284 - <ClassComponent />
285 - </StrictMode>,
286 - );
287 - instance.setState(state => {
288 - setStateCount++;
289 - return {
290 - count: state.count + 1,
291 - };
292 - });
293 -
294 - // Callback should be invoked twice (in DEV)
295 - expect(setStateCount).toBe(
296 - __DEV__ && debugRenderPhaseSideEffectsForStrictMode ? 2 : 1,
297 - );
298 - // But each time `state` should be the previous value
299 - expect(instance.state.count).toBe(2);
300 - });
301 - });
302 - });
303 -
304 - describe('Concurrent Mode', () => {
305 - beforeEach(() => {
306 - jest.resetModules();
307 -
308 - React = require('react');
309 - Scheduler = require('scheduler');
310 - ReactTestRenderer = require('react-test-renderer');
311 - });
312 -
313 - it('should warn about unsafe legacy lifecycle methods anywhere in the tree', () => {
314 - class AsyncRoot extends React.Component {
315 - UNSAFE_componentWillMount() {}
316 - UNSAFE_componentWillUpdate() {}
317 - render() {
318 - return (
319 - <div>
320 - <Wrapper>
321 - <Foo />
322 - </Wrapper>
323 - <div>
324 - <Bar />
325 - <Foo />
326 - </div>
327 - </div>
328 - );
329 - }
330 - }
331 - function Wrapper({children}) {
332 - return <div>{children}</div>;
333 - }
334 - class Foo extends React.Component {
335 - UNSAFE_componentWillReceiveProps() {}
336 - render() {
337 - return null;
338 - }
339 - }
340 - class Bar extends React.Component {
341 - UNSAFE_componentWillReceiveProps() {}
342 - render() {
343 - return null;
344 - }
345 - }
346 -
347 - const root = ReactTestRenderer.create(null, {
348 - unstable_isConcurrent: true,
349 - });
350 - root.update(<AsyncRoot />);
351 - expect(() => Scheduler.unstable_flushAll()).toWarnDev(
352 - [
353 - /* eslint-disable max-len */
354 - `Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
355 -
356 -* Move code with side effects to componentDidMount, and set initial state in the constructor.
357 -
358 -Please update the following components: AsyncRoot`,
359 - `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
360 -
361 -* Move data fetching code or side effects to componentDidUpdate.
362 -* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
363 -
364 -Please update the following components: Bar, Foo`,
365 - `Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
366 -
367 -* Move data fetching code or side effects to componentDidUpdate.
368 -
369 -Please update the following components: AsyncRoot`,
370 - /* eslint-enable max-len */
371 - ],
372 - {withoutStack: true},
373 - );
374 -
375 - // Dedupe
376 - root.update(<AsyncRoot />);
377 - Scheduler.unstable_flushAll();
378 - });
379 -
380 - it('should coalesce warnings by lifecycle name', () => {
381 - class AsyncRoot extends React.Component {
382 - UNSAFE_componentWillMount() {}
383 - UNSAFE_componentWillUpdate() {}
384 - render() {
385 - return <Parent />;
386 - }
387 - }
388 - class Parent extends React.Component {
389 - componentWillMount() {}
390 - componentWillUpdate() {}
391 - componentWillReceiveProps() {}
392 - render() {
393 - return <Child />;
394 - }
395 - }
396 - class Child extends React.Component {
397 - UNSAFE_componentWillReceiveProps() {}
398 - render() {
399 - return null;
400 - }
401 - }
402 -
403 - const root = ReactTestRenderer.create(null, {
404 - unstable_isConcurrent: true,
405 - });
406 - root.update(<AsyncRoot />);
407 -
408 - expect(() => {
409 - expect(() => Scheduler.unstable_flushAll()).toWarnDev(
410 - [
411 - /* eslint-disable max-len */
412 - `Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
413 -
414 -* Move code with side effects to componentDidMount, and set initial state in the constructor.
415 -
416 -Please update the following components: AsyncRoot`,
417 - `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
418 -
419 -* Move data fetching code or side effects to componentDidUpdate.
420 -* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
421 -
422 -Please update the following components: Child`,
423 - `Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
424 -
425 -* Move data fetching code or side effects to componentDidUpdate.
426 -
427 -Please update the following components: AsyncRoot`,
428 - /* eslint-enable max-len */
429 - ],
430 - {withoutStack: true},
431 - );
432 - }).toLowPriorityWarnDev(
433 - [
434 - /* eslint-disable max-len */
435 - `Warning: componentWillMount has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
436 -
437 -* Move code with side effects to componentDidMount, and set initial state in the constructor.
438 -* Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
439 -
440 -Please update the following components: Parent`,
441 - `Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
442 -
443 -* Move data fetching code or side effects to componentDidUpdate.
444 -* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
445 -* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
446 -
447 -Please update the following components: Parent`,
448 - `Warning: componentWillUpdate has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
449 -
450 -* Move data fetching code or side effects to componentDidUpdate.
451 -* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
452 -
453 -Please update the following components: Parent`,
454 - /* eslint-enable max-len */
455 - ],
456 - {withoutStack: true},
457 - );
458 - // Dedupe
459 - root.update(<AsyncRoot />);
460 - Scheduler.unstable_flushAll();
461 - });
462 -
463 - it('should warn about components not present during the initial render', () => {
464 - class AsyncRoot extends React.Component {
465 - render() {
466 - return this.props.foo ? <Foo /> : <Bar />;
467 - }
468 - }
469 - class Foo extends React.Component {
470 - UNSAFE_componentWillMount() {}
471 - render() {
472 - return null;
473 - }
474 - }
475 - class Bar extends React.Component {
476 - UNSAFE_componentWillMount() {}
477 - render() {
478 - return null;
479 - }
480 - }
481 -
482 - const root = ReactTestRenderer.create(null, {
483 - unstable_isConcurrent: true,
484 - });
485 - root.update(<AsyncRoot foo={true} />);
486 - expect(() => Scheduler.unstable_flushAll()).toWarnDev(
487 - 'Using UNSAFE_componentWillMount in strict mode is not recommended',
488 - {withoutStack: true},
489 - );
490 -
491 - root.update(<AsyncRoot foo={false} />);
492 - expect(() => Scheduler.unstable_flushAll()).toWarnDev(
493 - 'Using UNSAFE_componentWillMount in strict mode is not recommended',
494 - {withoutStack: true},
495 - );
496 -
497 - // Dedupe
498 - root.update(<AsyncRoot foo={true} />);
499 - Scheduler.unstable_flushAll();
500 - root.update(<AsyncRoot foo={false} />);
501 - Scheduler.unstable_flushAll();
502 - });
503 -
504 - it('should also warn inside of "strict" mode trees', () => {
505 - const {StrictMode} = React;
506 -
507 - class SyncRoot extends React.Component {
508 - UNSAFE_componentWillMount() {}
509 - UNSAFE_componentWillUpdate() {}
510 - UNSAFE_componentWillReceiveProps() {}
511 - render() {
512 - return (
513 - <StrictMode>
514 - <Wrapper />
515 - </StrictMode>
516 - );
517 - }
518 - }
519 - function Wrapper({children}) {
520 - return (
521 - <div>
522 - <Bar />
523 - <Foo />
524 - </div>
525 - );
526 - }
527 - class Foo extends React.Component {
528 - UNSAFE_componentWillReceiveProps() {}
529 - render() {
530 - return null;
531 - }
532 - }
533 - class Bar extends React.Component {
534 - UNSAFE_componentWillReceiveProps() {}
535 - render() {
536 - return null;
537 - }
538 - }
539 -
540 - expect(() => ReactTestRenderer.create(<SyncRoot />)).toWarnDev(
541 - 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
542 - {withoutStack: true},
543 - );
544 -
545 - // Dedupe
546 - const rendered = ReactTestRenderer.create(<SyncRoot />);
547 - rendered.update(<SyncRoot />);
548 - });
549 - });
550 -
551 - describe('symbol checks', () => {
552 - beforeEach(() => {
553 - jest.resetModules();
554 - React = require('react');
555 - ReactTestRenderer = require('react-test-renderer');
556 - });
557 -
558 - it('should switch from StrictMode to a Fragment and reset state', () => {
559 - const {Fragment, StrictMode} = React;
560 -
561 - function ParentComponent({useFragment}) {
562 - return useFragment ? (
563 - <Fragment>
564 - <ChildComponent />
565 - </Fragment>
566 - ) : (
567 - <StrictMode>
568 - <ChildComponent />
569 - </StrictMode>
570 - );
571 - }
572 -
573 - class ChildComponent extends React.Component {
574 - state = {
575 - count: 0,
576 - };
577 - static getDerivedStateFromProps(nextProps, prevState) {
578 - return {
579 - count: prevState.count + 1,
580 - };
581 - }
582 - render() {
583 - return `count:${this.state.count}`;
584 - }
585 - }
586 -
587 - const rendered = ReactTestRenderer.create(
588 - <ParentComponent useFragment={false} />,
589 - );
590 - expect(rendered.toJSON()).toBe('count:1');
591 - rendered.update(<ParentComponent useFragment={true} />);
592 - expect(rendered.toJSON()).toBe('count:1');
593 - });
594 -
595 - it('should switch from a Fragment to StrictMode and reset state', () => {
596 - const {Fragment, StrictMode} = React;
597 -
598 - function ParentComponent({useFragment}) {
599 - return useFragment ? (
600 - <Fragment>
601 - <ChildComponent />
602 - </Fragment>
603 - ) : (
604 - <StrictMode>
605 - <ChildComponent />
606 - </StrictMode>
607 - );
608 - }
609 -
610 - class ChildComponent extends React.Component {
611 - state = {
612 - count: 0,
613 - };
614 - static getDerivedStateFromProps(nextProps, prevState) {
615 - return {
616 - count: prevState.count + 1,
617 - };
618 - }
619 - render() {
620 - return `count:${this.state.count}`;
621 - }
622 - }
623 -
624 - const rendered = ReactTestRenderer.create(
625 - <ParentComponent useFragment={true} />,
626 - );
627 - expect(rendered.toJSON()).toBe('count:1');
628 - rendered.update(<ParentComponent useFragment={false} />);
629 - expect(rendered.toJSON()).toBe('count:1');
630 - });
631 -
632 - it('should update with StrictMode without losing state', () => {
633 - const {StrictMode} = React;
634 -
635 - function ParentComponent() {
636 - return (
637 - <StrictMode>
638 - <ChildComponent />
639 - </StrictMode>
640 - );
641 - }
642 -
643 - class ChildComponent extends React.Component {
644 - state = {
645 - count: 0,
646 - };
647 - static getDerivedStateFromProps(nextProps, prevState) {
648 - return {
649 - count: prevState.count + 1,
650 - };
651 - }
652 - render() {
653 - return `count:${this.state.count}`;
654 - }
655 - }
656 -
657 - const rendered = ReactTestRenderer.create(<ParentComponent />);
658 - expect(rendered.toJSON()).toBe('count:1');
659 - rendered.update(<ParentComponent />);
660 - expect(rendered.toJSON()).toBe('count:2');
661 - });
662 - });
663 -
664 - describe('string refs', () => {
665 - beforeEach(() => {
666 - jest.resetModules();
667 - React = require('react');
668 - ReactTestRenderer = require('react-test-renderer');
669 - });
670 -
671 - it('should warn within a strict tree', () => {
672 - const {StrictMode} = React;
673 -
674 - class OuterComponent extends React.Component {
675 - render() {
676 - return (
677 - <StrictMode>
678 - <InnerComponent ref="somestring" />
679 - </StrictMode>
680 - );
681 - }
682 - }
683 -
684 - class InnerComponent extends React.Component {
685 - render() {
686 - return null;
687 - }
688 - }
689 -
690 - let renderer;
691 - expect(() => {
692 - renderer = ReactTestRenderer.create(<OuterComponent />);
693 - }).toWarnDev(
694 - 'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
695 - 'String refs are a source of potential bugs and should be avoided. ' +
696 - 'We recommend using useRef() or createRef() instead. ' +
697 - 'Learn more about using refs safely here: ' +
698 - 'https://fb.me/react-strict-mode-string-ref\n' +
699 - ' in StrictMode (at **)\n' +
700 - ' in OuterComponent (at **)',
701 - );
702 -
703 - // Dedup
704 - renderer.update(<OuterComponent />);
705 - });
706 -
707 - it('should warn within a strict tree', () => {
708 - const {StrictMode} = React;
709 -
710 - class OuterComponent extends React.Component {
711 - render() {
712 - return (
713 - <StrictMode>
714 - <InnerComponent />
715 - </StrictMode>
716 - );
717 - }
718 - }
719 -
720 - class InnerComponent extends React.Component {
721 - render() {
722 - return <MiddleComponent ref="somestring" />;
723 - }
724 - }
725 -
726 - class MiddleComponent extends React.Component {
727 - render() {
728 - return null;
729 - }
730 - }
731 -
732 - let renderer;
733 - expect(() => {
734 - renderer = ReactTestRenderer.create(<OuterComponent />);
735 - }).toWarnDev(
736 - 'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
737 - 'String refs are a source of potential bugs and should be avoided. ' +
738 - 'We recommend using useRef() or createRef() instead. ' +
739 - 'Learn more about using refs safely here: ' +
740 - 'https://fb.me/react-strict-mode-string-ref\n' +
741 - ' in InnerComponent (at **)\n' +
742 - ' in StrictMode (at **)\n' +
743 - ' in OuterComponent (at **)',
744 - );
745 -
746 - // Dedup
747 - renderer.update(<OuterComponent />);
748 - });
749 - });
750 -
751 - describe('context legacy', () => {
752 - beforeEach(() => {
753 - jest.resetModules();
754 - React = require('react');
755 - ReactTestRenderer = require('react-test-renderer');
756 - PropTypes = require('prop-types');
757 - });
758 -
759 - it('should warn if the legacy context API have been used in strict mode', () => {
760 - class LegacyContextProvider extends React.Component {
761 - getChildContext() {
762 - return {color: 'purple'};
763 - }
764 -
765 - render() {
766 - return (
767 - <div>
768 - <LegacyContextConsumer />
769 - <FunctionalLegacyContextConsumer />
770 - </div>
771 - );
772 - }
773 - }
774 -
775 - function FunctionalLegacyContextConsumer() {
776 - return null;
777 - }
778 -
779 - LegacyContextProvider.childContextTypes = {
780 - color: PropTypes.string,
781 - };
782 -
783 - class LegacyContextConsumer extends React.Component {
784 - render() {
785 - return null;
786 - }
787 - }
788 -
789 - const {StrictMode} = React;
790 -
791 - class Root extends React.Component {
792 - render() {
793 - return (
794 - <div>
795 - <StrictMode>
796 - <LegacyContextProvider />
797 - </StrictMode>
798 - </div>
799 - );
800 - }
801 - }
802 -
803 - LegacyContextConsumer.contextTypes = {
804 - color: PropTypes.string,
805 - };
806 -
807 - FunctionalLegacyContextConsumer.contextTypes = {
808 - color: PropTypes.string,
809 - };
810 -
811 - let rendered;
812 -
813 - expect(() => {
814 - rendered = ReactTestRenderer.create(<Root />);
815 - }).toWarnDev(
816 - 'Warning: Legacy context API has been detected within a strict-mode tree.' +
817 - '\n\nThe old API will be supported in all 16.x releases, but applications ' +
818 - 'using it should migrate to the new version.' +
819 - '\n\nPlease update the following components: ' +
820 - 'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' +
821 - '\n\nLearn more about this warning here: ' +
822 - 'https://fb.me/react-legacy-context' +
823 - '\n in StrictMode (at **)' +
824 - '\n in div (at **)' +
825 - '\n in Root (at **)',
826 - );
827 -
828 - // Dedupe
829 - rendered = ReactTestRenderer.create(<Root />);
830 - rendered.update(<Root />);
831 - });
832 - });
833 -});
packages/react/src/__tests__/ReactStrictMode-test.js
+818
@@ -12,6 +12,8 @@
12 let React;
13 let ReactDOM;
14 let ReactDOMServer;
15 +let Scheduler;
16 +let PropTypes;
17
18 describe('ReactStrictMode', () => {
19 beforeEach(() => {
@@ -62,4 +64,820 @@ describe('ReactStrictMode', () => {
64 ' in StrictMode (at **)',
65 );
66 });
67 +
68 + it('should invoke precommit lifecycle methods twice', () => {
69 + let log = [];
70 + let shouldComponentUpdate = false;
71 + class ClassComponent extends React.Component {
72 + state = {};
73 + static getDerivedStateFromProps() {
74 + log.push('getDerivedStateFromProps');
75 + return null;
76 + }
77 + constructor(props) {
78 + super(props);
79 + log.push('constructor');
80 + }
81 + componentDidMount() {
82 + log.push('componentDidMount');
83 + }
84 + componentDidUpdate() {
85 + log.push('componentDidUpdate');
86 + }
87 + componentWillUnmount() {
88 + log.push('componentWillUnmount');
89 + }
90 + shouldComponentUpdate() {
91 + log.push('shouldComponentUpdate');
92 + return shouldComponentUpdate;
93 + }
94 + render() {
95 + log.push('render');
96 + return null;
97 + }
98 + }
99 +
100 + const container = document.createElement('div');
101 + ReactDOM.render(
102 + <React.StrictMode>
103 + <ClassComponent />
104 + </React.StrictMode>,
105 + container,
106 + );
107 +
108 + if (__DEV__) {
109 + expect(log).toEqual([
110 + 'constructor',
111 + 'constructor',
112 + 'getDerivedStateFromProps',
113 + 'getDerivedStateFromProps',
114 + 'render',
115 + 'render',
116 + 'componentDidMount',
117 + ]);
118 + } else {
119 + expect(log).toEqual([
120 + 'constructor',
121 + 'getDerivedStateFromProps',
122 + 'render',
123 + 'componentDidMount',
124 + ]);
125 + }
126 +
127 + log = [];
128 + shouldComponentUpdate = true;
129 +
130 + ReactDOM.render(
131 + <React.StrictMode>
132 + <ClassComponent />
133 + </React.StrictMode>,
134 + container,
135 + );
136 + if (__DEV__) {
137 + expect(log).toEqual([
138 + 'getDerivedStateFromProps',
139 + 'getDerivedStateFromProps',
140 + 'shouldComponentUpdate',
141 + 'render',
142 + 'render',
143 + 'componentDidUpdate',
144 + ]);
145 + } else {
146 + expect(log).toEqual([
147 + 'getDerivedStateFromProps',
148 + 'shouldComponentUpdate',
149 + 'render',
150 + 'componentDidUpdate',
151 + ]);
152 + }
153 +
154 + log = [];
155 + shouldComponentUpdate = false;
156 +
157 + ReactDOM.render(
158 + <React.StrictMode>
159 + <ClassComponent />
160 + </React.StrictMode>,
161 + container,
162 + );
163 +
164 + if (__DEV__) {
165 + expect(log).toEqual([
166 + 'getDerivedStateFromProps',
167 + 'getDerivedStateFromProps',
168 + 'shouldComponentUpdate',
169 + ]);
170 + } else {
171 + expect(log).toEqual([
172 + 'getDerivedStateFromProps',
173 + 'shouldComponentUpdate',
174 + ]);
175 + }
176 + });
177 +
178 + it('should invoke setState callbacks twice', () => {
179 + let instance;
180 + class ClassComponent extends React.Component {
181 + state = {
182 + count: 1,
183 + };
184 + render() {
185 + instance = this;
186 + return null;
187 + }
188 + }
189 +
190 + let setStateCount = 0;
191 +
192 + const container = document.createElement('div');
193 + ReactDOM.render(
194 + <React.StrictMode>
195 + <ClassComponent />
196 + </React.StrictMode>,
197 + container,
198 + );
199 + instance.setState(state => {
200 + setStateCount++;
201 + return {
202 + count: state.count + 1,
203 + };
204 + });
205 +
206 + // Callback should be invoked twice in DEV
207 + expect(setStateCount).toBe(__DEV__ ? 2 : 1);
208 + // But each time `state` should be the previous value
209 + expect(instance.state.count).toBe(2);
210 + });
211 +
212 + it('should invoke precommit lifecycle methods twice in DEV', () => {
213 + const {StrictMode} = React;
214 +
215 + let log = [];
216 + let shouldComponentUpdate = false;
217 +
218 + function Root() {
219 + return (
220 + <StrictMode>
221 + <ClassComponent />
222 + </StrictMode>
223 + );
224 + }
225 +
226 + class ClassComponent extends React.Component {
227 + state = {};
228 + static getDerivedStateFromProps() {
229 + log.push('getDerivedStateFromProps');
230 + return null;
231 + }
232 + constructor(props) {
233 + super(props);
234 + log.push('constructor');
235 + }
236 + componentDidMount() {
237 + log.push('componentDidMount');
238 + }
239 + componentDidUpdate() {
240 + log.push('componentDidUpdate');
241 + }
242 + componentWillUnmount() {
243 + log.push('componentWillUnmount');
244 + }
245 + shouldComponentUpdate() {
246 + log.push('shouldComponentUpdate');
247 + return shouldComponentUpdate;
248 + }
249 + render() {
250 + log.push('render');
251 + return null;
252 + }
253 + }
254 +
255 + const container = document.createElement('div');
256 + ReactDOM.render(<Root />, container);
257 +
258 + if (__DEV__) {
259 + expect(log).toEqual([
260 + 'constructor',
261 + 'constructor',
262 + 'getDerivedStateFromProps',
263 + 'getDerivedStateFromProps',
264 + 'render',
265 + 'render',
266 + 'componentDidMount',
267 + ]);
268 + } else {
269 + expect(log).toEqual([
270 + 'constructor',
271 + 'getDerivedStateFromProps',
272 + 'render',
273 + 'componentDidMount',
274 + ]);
275 + }
276 +
277 + log = [];
278 + shouldComponentUpdate = true;
279 +
280 + ReactDOM.render(<Root />, container);
281 + if (__DEV__) {
282 + expect(log).toEqual([
283 + 'getDerivedStateFromProps',
284 + 'getDerivedStateFromProps',
285 + 'shouldComponentUpdate',
286 + 'render',
287 + 'render',
288 + 'componentDidUpdate',
289 + ]);
290 + } else {
291 + expect(log).toEqual([
292 + 'getDerivedStateFromProps',
293 + 'shouldComponentUpdate',
294 + 'render',
295 + 'componentDidUpdate',
296 + ]);
297 + }
298 +
299 + log = [];
300 + shouldComponentUpdate = false;
301 +
302 + ReactDOM.render(<Root />, container);
303 + if (__DEV__) {
304 + expect(log).toEqual([
305 + 'getDerivedStateFromProps',
306 + 'getDerivedStateFromProps',
307 + 'shouldComponentUpdate',
308 + ]);
309 + } else {
310 + expect(log).toEqual([
311 + 'getDerivedStateFromProps',
312 + 'shouldComponentUpdate',
313 + ]);
314 + }
315 + });
316 +
317 + it('should invoke setState callbacks twice in DEV', () => {
318 + const {StrictMode} = React;
319 +
320 + let instance;
321 + class ClassComponent extends React.Component {
322 + state = {
323 + count: 1,
324 + };
325 + render() {
326 + instance = this;
327 + return null;
328 + }
329 + }
330 +
331 + let setStateCount = 0;
332 +
333 + const container = document.createElement('div');
334 + ReactDOM.render(
335 + <StrictMode>
336 + <ClassComponent />
337 + </StrictMode>,
338 + container,
339 + );
340 + instance.setState(state => {
341 + setStateCount++;
342 + return {
343 + count: state.count + 1,
344 + };
345 + });
346 +
347 + // Callback should be invoked twice (in DEV)
348 + expect(setStateCount).toBe(__DEV__ ? 2 : 1);
349 + // But each time `state` should be the previous value
350 + expect(instance.state.count).toBe(2);
351 + });
352 +});
353 +
354 +describe('Concurrent Mode', () => {
355 + beforeEach(() => {
356 + jest.resetModules();
357 +
358 + React = require('react');
359 + ReactDOM = require('react-dom');
360 + Scheduler = require('scheduler');
361 + });
362 +
363 + it.experimental(
364 + 'should warn about unsafe legacy lifecycle methods anywhere in the tree',
365 + () => {
366 + class AsyncRoot extends React.Component {
367 + UNSAFE_componentWillMount() {}
368 + UNSAFE_componentWillUpdate() {}
369 + render() {
370 + return (
371 + <div>
372 + <Wrapper>
373 + <Foo />
374 + </Wrapper>
375 + <div>
376 + <Bar />
377 + <Foo />
378 + </div>
379 + </div>
380 + );
381 + }
382 + }
383 + function Wrapper({children}) {
384 + return <div>{children}</div>;
385 + }
386 + class Foo extends React.Component {
387 + UNSAFE_componentWillReceiveProps() {}
388 + render() {
389 + return null;
390 + }
391 + }
392 + class Bar extends React.Component {
393 + UNSAFE_componentWillReceiveProps() {}
394 + render() {
395 + return null;
396 + }
397 + }
398 +
399 + const container = document.createElement('div');
400 + const root = ReactDOM.createRoot(container);
401 + root.render(<AsyncRoot />);
402 + expect(() => Scheduler.unstable_flushAll()).toWarnDev(
403 + [
404 + /* eslint-disable max-len */
405 + `Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
406 +
407 +* Move code with side effects to componentDidMount, and set initial state in the constructor.
408 +
409 +Please update the following components: AsyncRoot`,
410 + `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
411 +
412 +* Move data fetching code or side effects to componentDidUpdate.
413 +* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
414 +
415 +Please update the following components: Bar, Foo`,
416 + `Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
417 +
418 +* Move data fetching code or side effects to componentDidUpdate.
419 +
420 +Please update the following components: AsyncRoot`,
421 + /* eslint-enable max-len */
422 + ],
423 + {withoutStack: true},
424 + );
425 +
426 + // Dedupe
427 + root.render(<AsyncRoot />);
428 + Scheduler.unstable_flushAll();
429 + },
430 + );
431 +
432 + it.experimental('should coalesce warnings by lifecycle name', () => {
433 + class AsyncRoot extends React.Component {
434 + UNSAFE_componentWillMount() {}
435 + UNSAFE_componentWillUpdate() {}
436 + render() {
437 + return <Parent />;
438 + }
439 + }
440 + class Parent extends React.Component {
441 + componentWillMount() {}
442 + componentWillUpdate() {}
443 + componentWillReceiveProps() {}
444 + render() {
445 + return <Child />;
446 + }
447 + }
448 + class Child extends React.Component {
449 + UNSAFE_componentWillReceiveProps() {}
450 + render() {
451 + return null;
452 + }
453 + }
454 +
455 + const container = document.createElement('div');
456 + const root = ReactDOM.createRoot(container);
457 + root.render(<AsyncRoot />);
458 +
459 + expect(() => {
460 + expect(() => Scheduler.unstable_flushAll()).toWarnDev(
461 + [
462 + /* eslint-disable max-len */
463 + `Warning: Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
464 +
465 +* Move code with side effects to componentDidMount, and set initial state in the constructor.
466 +
467 +Please update the following components: AsyncRoot`,
468 + `Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
469 +
470 +* Move data fetching code or side effects to componentDidUpdate.
471 +* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
472 +
473 +Please update the following components: Child`,
474 + `Warning: Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
475 +
476 +* Move data fetching code or side effects to componentDidUpdate.
477 +
478 +Please update the following components: AsyncRoot`,
479 + /* eslint-enable max-len */
480 + ],
481 + {withoutStack: true},
482 + );
483 + }).toLowPriorityWarnDev(
484 + [
485 + /* eslint-disable max-len */
486 + `Warning: componentWillMount has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
487 +
488 +* Move code with side effects to componentDidMount, and set initial state in the constructor.
489 +* Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
490 +
491 +Please update the following components: Parent`,
492 + `Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
493 +
494 +* Move data fetching code or side effects to componentDidUpdate.
495 +* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
496 +* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
497 +
498 +Please update the following components: Parent`,
499 + `Warning: componentWillUpdate has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
500 +
501 +* Move data fetching code or side effects to componentDidUpdate.
502 +* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
503 +
504 +Please update the following components: Parent`,
505 + /* eslint-enable max-len */
506 + ],
507 + {withoutStack: true},
508 + );
509 + // Dedupe
510 + root.render(<AsyncRoot />);
511 + Scheduler.unstable_flushAll();
512 + });
513 +
514 + it.experimental(
515 + 'should warn about components not present during the initial render',
516 + () => {
517 + class AsyncRoot extends React.Component {
518 + render() {
519 + return this.props.foo ? <Foo /> : <Bar />;
520 + }
521 + }
522 + class Foo extends React.Component {
523 + UNSAFE_componentWillMount() {}
524 + render() {
525 + return null;
526 + }
527 + }
528 + class Bar extends React.Component {
529 + UNSAFE_componentWillMount() {}
530 + render() {
531 + return null;
532 + }
533 + }
534 +
535 + const container = document.createElement('div');
536 + const root = ReactDOM.createRoot(container);
537 + root.render(<AsyncRoot foo={true} />);
538 + expect(() => Scheduler.unstable_flushAll()).toWarnDev(
539 + 'Using UNSAFE_componentWillMount in strict mode is not recommended',
540 + {withoutStack: true},
541 + );
542 +
543 + root.render(<AsyncRoot foo={false} />);
544 + expect(() => Scheduler.unstable_flushAll()).toWarnDev(
545 + 'Using UNSAFE_componentWillMount in strict mode is not recommended',
546 + {withoutStack: true},
547 + );
548 +
549 + // Dedupe
550 + root.render(<AsyncRoot foo={true} />);
551 + Scheduler.unstable_flushAll();
552 + root.render(<AsyncRoot foo={false} />);
553 + Scheduler.unstable_flushAll();
554 + },
555 + );
556 +
557 + it('should also warn inside of "strict" mode trees', () => {
558 + const {StrictMode} = React;
559 +
560 + class SyncRoot extends React.Component {
561 + UNSAFE_componentWillMount() {}
562 + UNSAFE_componentWillUpdate() {}
563 + UNSAFE_componentWillReceiveProps() {}
564 + render() {
565 + return (
566 + <StrictMode>
567 + <Wrapper />
568 + </StrictMode>
569 + );
570 + }
571 + }
572 + function Wrapper({children}) {
573 + return (
574 + <div>
575 + <Bar />
576 + <Foo />
577 + </div>
578 + );
579 + }
580 + class Foo extends React.Component {
581 + UNSAFE_componentWillReceiveProps() {}
582 + render() {
583 + return null;
584 + }
585 + }
586 + class Bar extends React.Component {
587 + UNSAFE_componentWillReceiveProps() {}
588 + render() {
589 + return null;
590 + }
591 + }
592 +
593 + const container = document.createElement('div');
594 +
595 + expect(() => ReactDOM.render(<SyncRoot />, container)).toWarnDev(
596 + 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
597 + {withoutStack: true},
598 + );
599 +
600 + // Dedupe
601 + ReactDOM.render(<SyncRoot />, container);
602 + });
603 +});
604 +
605 +describe('symbol checks', () => {
606 + beforeEach(() => {
607 + jest.resetModules();
608 + React = require('react');
609 + ReactDOM = require('react-dom');
610 + });
611 +
612 + it('should switch from StrictMode to a Fragment and reset state', () => {
613 + const {Fragment, StrictMode} = React;
614 +
615 + function ParentComponent({useFragment}) {
616 + return useFragment ? (
617 + <Fragment>
618 + <ChildComponent />
619 + </Fragment>
620 + ) : (
621 + <StrictMode>
622 + <ChildComponent />
623 + </StrictMode>
624 + );
625 + }
626 +
627 + class ChildComponent extends React.Component {
628 + state = {
629 + count: 0,
630 + };
631 + static getDerivedStateFromProps(nextProps, prevState) {
632 + return {
633 + count: prevState.count + 1,
634 + };
635 + }
636 + render() {
637 + return `count:${this.state.count}`;
638 + }
639 + }
640 +
641 + const container = document.createElement('div');
642 + ReactDOM.render(<ParentComponent useFragment={false} />, container);
643 + expect(container.textContent).toBe('count:1');
644 + ReactDOM.render(<ParentComponent useFragment={true} />, container);
645 + expect(container.textContent).toBe('count:1');
646 + });
647 +
648 + it('should switch from a Fragment to StrictMode and reset state', () => {
649 + const {Fragment, StrictMode} = React;
650 +
651 + function ParentComponent({useFragment}) {
652 + return useFragment ? (
653 + <Fragment>
654 + <ChildComponent />
655 + </Fragment>
656 + ) : (
657 + <StrictMode>
658 + <ChildComponent />
659 + </StrictMode>
660 + );
661 + }
662 +
663 + class ChildComponent extends React.Component {
664 + state = {
665 + count: 0,
666 + };
667 + static getDerivedStateFromProps(nextProps, prevState) {
668 + return {
669 + count: prevState.count + 1,
670 + };
671 + }
672 + render() {
673 + return `count:${this.state.count}`;
674 + }
675 + }
676 +
677 + const container = document.createElement('div');
678 + ReactDOM.render(<ParentComponent useFragment={true} />, container);
679 + expect(container.textContent).toBe('count:1');
680 + ReactDOM.render(<ParentComponent useFragment={false} />, container);
681 + expect(container.textContent).toBe('count:1');
682 + });
683 +
684 + it('should update with StrictMode without losing state', () => {
685 + const {StrictMode} = React;
686 +
687 + function ParentComponent() {
688 + return (
689 + <StrictMode>
690 + <ChildComponent />
691 + </StrictMode>
692 + );
693 + }
694 +
695 + class ChildComponent extends React.Component {
696 + state = {
697 + count: 0,
698 + };
699 + static getDerivedStateFromProps(nextProps, prevState) {
700 + return {
701 + count: prevState.count + 1,
702 + };
703 + }
704 + render() {
705 + return `count:${this.state.count}`;
706 + }
707 + }
708 +
709 + const container = document.createElement('div');
710 + ReactDOM.render(<ParentComponent />, container);
711 + expect(container.textContent).toBe('count:1');
712 + ReactDOM.render(<ParentComponent />, container);
713 + expect(container.textContent).toBe('count:2');
714 + });
715 +});
716 +
717 +describe('string refs', () => {
718 + beforeEach(() => {
719 + jest.resetModules();
720 + React = require('react');
721 + ReactDOM = require('react-dom');
722 + });
723 +
724 + it('should warn within a strict tree', () => {
725 + const {StrictMode} = React;
726 +
727 + class OuterComponent extends React.Component {
728 + render() {
729 + return (
730 + <StrictMode>
731 + <InnerComponent ref="somestring" />
732 + </StrictMode>
733 + );
734 + }
735 + }
736 +
737 + class InnerComponent extends React.Component {
738 + render() {
739 + return null;
740 + }
741 + }
742 +
743 + const container = document.createElement('div');
744 + expect(() => {
745 + ReactDOM.render(<OuterComponent />, container);
746 + }).toWarnDev(
747 + 'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
748 + 'String refs are a source of potential bugs and should be avoided. ' +
749 + 'We recommend using useRef() or createRef() instead. ' +
750 + 'Learn more about using refs safely here: ' +
751 + 'https://fb.me/react-strict-mode-string-ref\n' +
752 + ' in StrictMode (at **)\n' +
753 + ' in OuterComponent (at **)',
754 + );
755 +
756 + // Dedup
757 + ReactDOM.render(<OuterComponent />, container);
758 + });
759 +
760 + it('should warn within a strict tree', () => {
761 + const {StrictMode} = React;
762 +
763 + class OuterComponent extends React.Component {
764 + render() {
765 + return (
766 + <StrictMode>
767 + <InnerComponent />
768 + </StrictMode>
769 + );
770 + }
771 + }
772 +
773 + class InnerComponent extends React.Component {
774 + render() {
775 + return <MiddleComponent ref="somestring" />;
776 + }
777 + }
778 +
779 + class MiddleComponent extends React.Component {
780 + render() {
781 + return null;
782 + }
783 + }
784 +
785 + const container = document.createElement('div');
786 + expect(() => {
787 + ReactDOM.render(<OuterComponent />, container);
788 + }).toWarnDev(
789 + 'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
790 + 'String refs are a source of potential bugs and should be avoided. ' +
791 + 'We recommend using useRef() or createRef() instead. ' +
792 + 'Learn more about using refs safely here: ' +
793 + 'https://fb.me/react-strict-mode-string-ref\n' +
794 + ' in InnerComponent (at **)\n' +
795 + ' in StrictMode (at **)\n' +
796 + ' in OuterComponent (at **)',
797 + );
798 +
799 + // Dedup
800 + ReactDOM.render(<OuterComponent />, container);
801 + });
802 +});
803 +
804 +describe('context legacy', () => {
805 + beforeEach(() => {
806 + jest.resetModules();
807 + React = require('react');
808 + ReactDOM = require('react-dom');
809 + PropTypes = require('prop-types');
810 + });
811 +
812 + it('should warn if the legacy context API have been used in strict mode', () => {
813 + class LegacyContextProvider extends React.Component {
814 + getChildContext() {
815 + return {color: 'purple'};
816 + }
817 +
818 + render() {
819 + return (
820 + <div>
821 + <LegacyContextConsumer />
822 + <FunctionalLegacyContextConsumer />
823 + </div>
824 + );
825 + }
826 + }
827 +
828 + function FunctionalLegacyContextConsumer() {
829 + return null;
830 + }
831 +
832 + LegacyContextProvider.childContextTypes = {
833 + color: PropTypes.string,
834 + };
835 +
836 + class LegacyContextConsumer extends React.Component {
837 + render() {
838 + return null;
839 + }
840 + }
841 +
842 + const {StrictMode} = React;
843 +
844 + class Root extends React.Component {
845 + render() {
846 + return (
847 + <div>
848 + <StrictMode>
849 + <LegacyContextProvider />
850 + </StrictMode>
851 + </div>
852 + );
853 + }
854 + }
855 +
856 + LegacyContextConsumer.contextTypes = {
857 + color: PropTypes.string,
858 + };
859 +
860 + FunctionalLegacyContextConsumer.contextTypes = {
861 + color: PropTypes.string,
862 + };
863 +
864 + const container = document.createElement('div');
865 + expect(() => {
866 + ReactDOM.render(<Root />, container);
867 + }).toWarnDev(
868 + 'Warning: Legacy context API has been detected within a strict-mode tree.' +
869 + '\n\nThe old API will be supported in all 16.x releases, but applications ' +
870 + 'using it should migrate to the new version.' +
871 + '\n\nPlease update the following components: ' +
872 + 'FunctionalLegacyContextConsumer, LegacyContextConsumer, LegacyContextProvider' +
873 + '\n\nLearn more about this warning here: ' +
874 + 'https://fb.me/react-legacy-context' +
875 + '\n in StrictMode (at **)' +
876 + '\n in div (at **)' +
877 + '\n in Root (at **)',
878 + );
879 +
880 + // Dedupe
881 + ReactDOM.render(<Root />, container);
882 + });
883 });
packages/shared/ReactFeatureFlags.js
+2 -7
@@ -9,13 +9,8 @@
9
10 export const enableUserTimingAPI = __DEV__;
11
12 -// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
13 -export const debugRenderPhaseSideEffects = false;
14 -
15 -// In some cases, StrictMode should also double-render lifecycles.
16 -// This can be confusing for tests though,
17 -// And it can be bad for performance in production.
18 -// This feature flag can be used to control the behavior:
12 +// Helps identify side effects in render-phase lifecycle hooks and setState
13 +// reducers by double invoking them in Strict Mode.
14 export const debugRenderPhaseSideEffectsForStrictMode = __DEV__;
15
16 // To preserve the "Pause on caught exceptions" behavior of the debugger, we
packages/shared/forks/ReactFeatureFlags.native-fb.js
+2 -4
@@ -12,10 +12,8 @@ import invariant from 'shared/invariant';
12 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
13 import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.native-fb';
14
15 -// Re-export dynamic flags from the fbsource version.
16 -export const {
17 - debugRenderPhaseSideEffects,
18 -} = require('../shims/ReactFeatureFlags');
15 +// Uncomment to re-export dynamic flags from the fbsource version.
16 +// export const {} = require('../shims/ReactFeatureFlags');
17
18 // The rest of the flags are static for better dead code elimination.
19 export const enableUserTimingAPI = __DEV__;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -12,7 +12,6 @@ import invariant from 'shared/invariant';
12 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
13 import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.native-oss';
14
15 -export const debugRenderPhaseSideEffects = false;
15 export const debugRenderPhaseSideEffectsForStrictMode = false;
16 export const enableUserTimingAPI = __DEV__;
17 export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
packages/shared/forks/ReactFeatureFlags.persistent.js
-1
@@ -12,7 +12,6 @@ import invariant from 'shared/invariant';
12 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
13 import typeof * as PersistentFeatureFlagsType from './ReactFeatureFlags.persistent';
14
15 -export const debugRenderPhaseSideEffects = false;
15 export const debugRenderPhaseSideEffectsForStrictMode = false;
16 export const enableUserTimingAPI = __DEV__;
17 export const warnAboutDeprecatedLifecycles = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -12,7 +12,6 @@ import invariant from 'shared/invariant';
12 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
13 import typeof * as PersistentFeatureFlagsType from './ReactFeatureFlags.persistent';
14
15 -export const debugRenderPhaseSideEffects = false;
15 export const debugRenderPhaseSideEffectsForStrictMode = false;
16 export const enableUserTimingAPI = __DEV__;
17 export const warnAboutDeprecatedLifecycles = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -12,7 +12,6 @@ import invariant from 'shared/invariant';
12 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
13 import typeof * as PersistentFeatureFlagsType from './ReactFeatureFlags.persistent';
14
15 -export const debugRenderPhaseSideEffects = false;
15 export const debugRenderPhaseSideEffectsForStrictMode = false;
16 export const enableUserTimingAPI = __DEV__;
17 export const warnAboutDeprecatedLifecycles = true;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -12,7 +12,6 @@ import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.www';
12
13 // Re-export dynamic flags from the www version.
14 export const {
15 - debugRenderPhaseSideEffects,
15 debugRenderPhaseSideEffectsForStrictMode,
16 disableInputAttributeSyncing,
17 enableTrustedTypesIntegration,