@samitouri / QOS-React-2 / commits / 83fc258f29

Remove <ConcurrentMode /> (#15532)

Use createSyncRoot instead.

Andrew Clark committed May 13, 2019 at 16:10 UTC 83fc258f2914c1a7c14c7b98ee362934576f266b
15 files changed +118 -686
fixtures/unstable-async/time-slicing/src/index.js
+5 -9
@@ -1,6 +1,6 @@
1 import React, {PureComponent} from 'react';
2 -import {flushSync, render} from 'react-dom';
3 -import {unstable_scheduleCallback} from 'scheduler';
2 +import {flushSync, unstable_createRoot} from 'react-dom';
3 +import Scheduler from 'scheduler';
4 import _ from 'lodash';
5 import Charts from './Charts';
6 import Clock from './Clock';
@@ -67,7 +67,7 @@ class App extends PureComponent {
67 }
68 this._ignoreClick = true;
69
70 - unstable_scheduleCallback(() => {
70 + Scheduler.unstable_next(() => {
71 this.setState({showDemo: true}, () => {
72 this._ignoreClick = false;
73 });
@@ -146,9 +146,5 @@ class App extends PureComponent {
146 }
147
148 const container = document.getElementById('root');
149 -render(
150 - <React.unstable_ConcurrentMode>
151 - <App />
152 - </React.unstable_ConcurrentMode>,
153 - container
154 -);
149 +const root = ReactDOM.unstable_createRoot(container);
150 +root.render(<App />, container);
packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.internal.js
+11 -100
@@ -16,8 +16,6 @@ let ReactFeatureFlags = require('shared/ReactFeatureFlags');
16 let ReactDOM;
17 let Scheduler;
18
19 -const ConcurrentMode = React.unstable_ConcurrentMode;
20 -
19 const setUntrackedInputValue = Object.getOwnPropertyDescriptor(
20 HTMLInputElement.prototype,
21 'value',
@@ -86,12 +84,9 @@ describe('ReactDOMFiberAsync', () => {
84 );
85 }
86 }
89 - ReactDOM.render(
90 - <ConcurrentMode>
91 - <Counter />
92 - </ConcurrentMode>,
93 - container,
94 - );
87 + const root = ReactDOM.unstable_createRoot(container);
88 + root.render(<Counter />);
89 + Scheduler.flushAll();
90 expect(asyncValueRef.current.textContent).toBe('');
91 expect(syncValueRef.current.textContent).toBe('');
92
@@ -108,34 +103,7 @@ describe('ReactDOMFiberAsync', () => {
103 expect(syncValueRef.current.textContent).toBe('hello');
104 });
105
111 - describe('with feature flag disabled', () => {
112 - beforeEach(() => {
113 - jest.resetModules();
114 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
115 - ReactDOM = require('react-dom');
116 - Scheduler = require('scheduler');
117 - });
118 -
119 - it('renders synchronously', () => {
120 - ReactDOM.render(
121 - <ConcurrentMode>
122 - <div>Hi</div>
123 - </ConcurrentMode>,
124 - container,
125 - );
126 - expect(container.textContent).toEqual('Hi');
127 -
128 - ReactDOM.render(
129 - <ConcurrentMode>
130 - <div>Bye</div>
131 - </ConcurrentMode>,
132 - container,
133 - );
134 - expect(container.textContent).toEqual('Bye');
135 - });
136 - });
137 -
138 - describe('with feature flag enabled', () => {
106 + describe('concurrent mode', () => {
107 beforeEach(() => {
108 jest.resetModules();
109 ReactFeatureFlags = require('shared/ReactFeatureFlags');
@@ -144,7 +112,7 @@ describe('ReactDOMFiberAsync', () => {
112 Scheduler = require('scheduler');
113 });
114
147 - it('createRoot makes the entire tree async', () => {
115 + it('top-level updates are concurrent', () => {
116 const root = ReactDOM.unstable_createRoot(container);
117 root.render(<div>Hi</div>);
118 expect(container.textContent).toEqual('');
@@ -157,7 +125,7 @@ describe('ReactDOMFiberAsync', () => {
125 expect(container.textContent).toEqual('Bye');
126 });
127
160 - it('updates inside an async tree are async by default', () => {
128 + it('deep updates (setState) are oncurrent', () => {
129 let instance;
130 class Component extends React.Component {
131 state = {step: 0};
@@ -179,56 +147,6 @@ describe('ReactDOMFiberAsync', () => {
147 expect(container.textContent).toEqual('1');
148 });
149
182 - it('ConcurrentMode creates an async subtree', () => {
183 - let instance;
184 - class Component extends React.Component {
185 - state = {step: 0};
186 - render() {
187 - instance = this;
188 - return <div>{this.state.step}</div>;
189 - }
190 - }
191 -
192 - ReactDOM.render(
193 - <ConcurrentMode>
194 - <Component />
195 - </ConcurrentMode>,
196 - container,
197 - );
198 - Scheduler.flushAll();
199 -
200 - instance.setState({step: 1});
201 - expect(container.textContent).toEqual('0');
202 - Scheduler.flushAll();
203 - expect(container.textContent).toEqual('1');
204 - });
205 -
206 - it('updates inside an async subtree are async by default', () => {
207 - let instance;
208 - class Child extends React.Component {
209 - state = {step: 0};
210 - render() {
211 - instance = this;
212 - return <div>{this.state.step}</div>;
213 - }
214 - }
215 -
216 - ReactDOM.render(
217 - <div>
218 - <ConcurrentMode>
219 - <Child />
220 - </ConcurrentMode>
221 - </div>,
222 - container,
223 - );
224 - Scheduler.flushAll();
225 -
226 - instance.setState({step: 1});
227 - expect(container.textContent).toEqual('0');
228 - Scheduler.flushAll();
229 - expect(container.textContent).toEqual('1');
230 - });
231 -
150 it('flushSync batches sync updates and flushes them at the end of the batch', () => {
151 let ops = [];
152 let instance;
@@ -345,12 +263,8 @@ describe('ReactDOMFiberAsync', () => {
263 }
264 }
265
348 - ReactDOM.render(
349 - <ConcurrentMode>
350 - <Component />
351 - </ConcurrentMode>,
352 - container,
353 - );
266 + const root = ReactDOM.unstable_createRoot(container);
267 + root.render(<Component />);
268 Scheduler.flushAll();
269
270 // Updates are async by default
@@ -390,12 +304,9 @@ describe('ReactDOMFiberAsync', () => {
304 return this.state.counter;
305 }
306 }
393 - ReactDOM.render(
394 - <ConcurrentMode>
395 - <Counter />
396 - </ConcurrentMode>,
397 - container,
398 - );
307 + const root = ReactDOM.unstable_createRoot(container);
308 + root.render(<Counter />);
309 + Scheduler.flushAll();
310 expect(container.textContent).toEqual('0');
311
312 // Test that a normal update is async
packages/react-dom/src/__tests__/ReactDOMHooks-test.js
+2 -6
@@ -141,7 +141,7 @@ describe('ReactDOMHooks', () => {
141 expect(labelRef.current.innerHTML).toBe('abc');
142 });
143
144 - it('should not bail out when an update is scheduled from within an event handler in ConcurrentMode', () => {
144 + it('should not bail out when an update is scheduled from within an event handler in Concurrent Mode', () => {
145 const {createRef, useCallback, useState} = React;
146
147 const Example = ({inputRef, labelRef}) => {
@@ -162,11 +162,7 @@ describe('ReactDOMHooks', () => {
162 const labelRef = createRef();
163
164 const root = ReactDOM.unstable_createRoot(container);
165 - root.render(
166 - <React.unstable_ConcurrentMode>
167 - <Example inputRef={inputRef} labelRef={labelRef} />
168 - </React.unstable_ConcurrentMode>,
169 - );
165 + root.render(<Example inputRef={inputRef} labelRef={labelRef} />);
166
167 Scheduler.flushAll();
168
packages/react-dom/src/__tests__/ReactDOMRoot-test.js
+5 -11
@@ -13,7 +13,6 @@ let React = require('react');
13 let ReactDOM = require('react-dom');
14 let ReactDOMServer = require('react-dom/server');
15 let Scheduler = require('scheduler');
16 -let ConcurrentMode = React.unstable_ConcurrentMode;
16
17 describe('ReactDOMRoot', () => {
18 let container;
@@ -25,7 +24,6 @@ describe('ReactDOMRoot', () => {
24 ReactDOM = require('react-dom');
25 ReactDOMServer = require('react-dom/server');
26 Scheduler = require('scheduler');
28 - ConcurrentMode = React.unstable_ConcurrentMode;
27 });
28
29 it('renders children', () => {
@@ -47,7 +45,7 @@ describe('ReactDOMRoot', () => {
45
46 it('`root.render` returns a thenable work object', () => {
47 const root = ReactDOM.unstable_createRoot(container);
50 - const work = root.render(<ConcurrentMode>Hi</ConcurrentMode>);
48 + const work = root.render('Hi');
49 let ops = [];
50 work.then(() => {
51 ops.push('inside callback: ' + container.textContent);
@@ -65,7 +63,7 @@ describe('ReactDOMRoot', () => {
63
64 it('resolves `work.then` callback synchronously if the work already committed', () => {
65 const root = ReactDOM.unstable_createRoot(container);
68 - const work = root.render(<ConcurrentMode>Hi</ConcurrentMode>);
66 + const work = root.render('Hi');
67 Scheduler.flushAll();
68 let ops = [];
69 work.then(() => {
@@ -157,11 +155,7 @@ describe('ReactDOMRoot', () => {
155
156 const root = ReactDOM.unstable_createRoot(container);
157 const batch = root.createBatch();
160 - batch.render(
161 - <ConcurrentMode>
162 - <App />
163 - </ConcurrentMode>,
164 - );
158 + batch.render(<App />);
159
160 Scheduler.flushAll();
161
@@ -208,7 +202,7 @@ describe('ReactDOMRoot', () => {
202 it('can wait for a batch to finish', () => {
203 const root = ReactDOM.unstable_createRoot(container);
204 const batch = root.createBatch();
211 - batch.render(<ConcurrentMode>Foo</ConcurrentMode>);
205 + batch.render('Foo');
206
207 Scheduler.flushAll();
208
@@ -248,7 +242,7 @@ describe('ReactDOMRoot', () => {
242
243 it('can commit an empty batch', () => {
244 const root = ReactDOM.unstable_createRoot(container);
251 - root.render(<ConcurrentMode>1</ConcurrentMode>);
245 + root.render(1);
246
247 Scheduler.advanceTime(2000);
248 // This batch has a later expiration time than the earlier update.
packages/react-dom/src/__tests__/ReactDOMServerIntegrationModes-test.js
-68
@@ -103,72 +103,4 @@ describe('ReactDOMServerIntegration', () => {
103 expect(await render(<React.StrictMode />)).toBe(null);
104 });
105 });
106 -
107 - describe('React.unstable_ConcurrentMode', () => {
108 - itRenders('an concurrent mode with one child', async render => {
109 - let e = await render(
110 - <React.unstable_ConcurrentMode>
111 - <div>text1</div>
112 - </React.unstable_ConcurrentMode>,
113 - );
114 - let parent = e.parentNode;
115 - expect(parent.childNodes[0].tagName).toBe('DIV');
116 - });
117 -
118 - itRenders('an concurrent mode with several children', async render => {
119 - let Header = props => {
120 - return <p>header</p>;
121 - };
122 - let Footer = props => {
123 - return (
124 - <React.unstable_ConcurrentMode>
125 - <h2>footer</h2>
126 - <h3>about</h3>
127 - </React.unstable_ConcurrentMode>
128 - );
129 - };
130 - let e = await render(
131 - <React.unstable_ConcurrentMode>
132 - <div>text1</div>
133 - <span>text2</span>
134 - <Header />
135 - <Footer />
136 - </React.unstable_ConcurrentMode>,
137 - );
138 - let parent = e.parentNode;
139 - expect(parent.childNodes[0].tagName).toBe('DIV');
140 - expect(parent.childNodes[1].tagName).toBe('SPAN');
141 - expect(parent.childNodes[2].tagName).toBe('P');
142 - expect(parent.childNodes[3].tagName).toBe('H2');
143 - expect(parent.childNodes[4].tagName).toBe('H3');
144 - });
145 -
146 - itRenders('a nested concurrent mode', async render => {
147 - let e = await render(
148 - <React.unstable_ConcurrentMode>
149 - <React.unstable_ConcurrentMode>
150 - <div>text1</div>
151 - </React.unstable_ConcurrentMode>
152 - <span>text2</span>
153 - <React.unstable_ConcurrentMode>
154 - <React.unstable_ConcurrentMode>
155 - <React.unstable_ConcurrentMode>
156 - {null}
157 - <p />
158 - </React.unstable_ConcurrentMode>
159 - {false}
160 - </React.unstable_ConcurrentMode>
161 - </React.unstable_ConcurrentMode>
162 - </React.unstable_ConcurrentMode>,
163 - );
164 - let parent = e.parentNode;
165 - expect(parent.childNodes[0].tagName).toBe('DIV');
166 - expect(parent.childNodes[1].tagName).toBe('SPAN');
167 - expect(parent.childNodes[2].tagName).toBe('P');
168 - });
169 -
170 - itRenders('an empty concurrent mode', async render => {
171 - expect(await render(<React.unstable_ConcurrentMode />)).toBe(null);
172 - });
173 - });
106 });
packages/react-dom/src/events/__tests__/SimpleEventPlugin-test.internal.js
+46 -19
@@ -374,7 +374,7 @@ describe('SimpleEventPlugin', function() {
374 expect(button.textContent).toEqual('Count: 7');
375 });
376
377 - it('flushes lowest pending interactive priority', () => {
377 + it('flushes discrete updates in order', () => {
378 container = document.createElement('div');
379 document.body.appendChild(container);
380
@@ -382,15 +382,21 @@ describe('SimpleEventPlugin', function() {
382 class Button extends React.Component {
383 state = {lowPriCount: 0};
384 render() {
385 + const text = `High-pri count: ${
386 + this.props.highPriCount
387 + }, Low-pri count: ${this.state.lowPriCount}`;
388 + Scheduler.yieldValue(text);
389 return (
390 <button
391 ref={el => (button = el)}
388 - onClick={
389 - // Intentionally not using the updater form here
390 - () => this.setState({lowPriCount: this.state.lowPriCount + 1})
391 - }>
392 - High-pri count: {this.props.highPriCount}, Low-pri count:{' '}
393 - {this.state.lowPriCount}
392 + onClick={() => {
393 + Scheduler.unstable_next(() => {
394 + this.setState(state => ({
395 + lowPriCount: state.lowPriCount + 1,
396 + }));
397 + });
398 + }}>
399 + {text}
400 </button>
401 );
402 }
@@ -402,19 +408,24 @@ describe('SimpleEventPlugin', function() {
408 return (
409 <div
410 onClick={
405 - // Intentionally not using the updater form here
406 - () => this.setState({highPriCount: this.state.highPriCount + 1})
411 + // Intentionally not using the updater form here, to test
412 + // that updates are serially processed.
413 + () => {
414 + this.setState({highPriCount: this.state.highPriCount + 1});
415 + }
416 }>
408 - <React.unstable_ConcurrentMode>
409 - <Button highPriCount={this.state.highPriCount} />
410 - </React.unstable_ConcurrentMode>
417 + <Button highPriCount={this.state.highPriCount} />
418 </div>
419 );
420 }
421 }
422
423 // Initial mount
417 - ReactDOM.render(<Wrapper />, container);
424 + const root = ReactDOM.unstable_createRoot(container);
425 + root.render(<Wrapper />);
426 + expect(Scheduler).toFlushAndYield([
427 + 'High-pri count: 0, Low-pri count: 0',
428 + ]);
429 expect(button.textContent).toEqual('High-pri count: 0, Low-pri count: 0');
430
431 function click() {
@@ -425,8 +436,12 @@ describe('SimpleEventPlugin', function() {
436
437 // Click the button a single time
438 click();
428 - // The high-pri counter should flush synchronously, but not the
429 - // low-pri counter
439 + // Nothing should flush on the first click.
440 + expect(Scheduler).toHaveYielded([]);
441 + // Click again. This will force the previous discrete update to flush. But
442 + // only the high-pri count will increase.
443 + click();
444 + expect(Scheduler).toHaveYielded(['High-pri count: 1, Low-pri count: 0']);
445 expect(button.textContent).toEqual('High-pri count: 1, Low-pri count: 0');
446
447 // Click the button many more times
@@ -437,10 +452,22 @@ describe('SimpleEventPlugin', function() {
452 click();
453 click();
454
440 - // Flush the remaining work
441 - Scheduler.flushAll();
442 - // Both counters should equal the total number of clicks
443 - expect(button.textContent).toEqual('High-pri count: 7, Low-pri count: 7');
455 + // Flush the remaining work.
456 + expect(Scheduler).toHaveYielded([
457 + 'High-pri count: 2, Low-pri count: 0',
458 + 'High-pri count: 3, Low-pri count: 0',
459 + 'High-pri count: 4, Low-pri count: 0',
460 + 'High-pri count: 5, Low-pri count: 0',
461 + 'High-pri count: 6, Low-pri count: 0',
462 + 'High-pri count: 7, Low-pri count: 0',
463 + ]);
464 +
465 + // At the end, both counters should equal the total number of clicks
466 + expect(Scheduler).toFlushAndYield([
467 + 'High-pri count: 8, Low-pri count: 0',
468 + 'High-pri count: 8, Low-pri count: 8',
469 + ]);
470 + expect(button.textContent).toEqual('High-pri count: 8, Low-pri count: 8');
471 });
472 });
473
packages/react-is/README.md
-10
@@ -45,16 +45,6 @@ ReactIs.isValidElementType(React.createFactory("div")); // true
45
46 ### Determining an Element's Type
47
48 -#### ConcurrentMode
49 -
50 -```js
51 -import React from "react";
52 -import * as ReactIs from 'react-is';
53 -
54 -ReactIs.isConcurrentMode(<React.unstable_ConcurrentMode />); // true
55 -ReactIs.typeOf(<React.unstable_ConcurrentMode />) === ReactIs.ConcurrentMode; // true
56 -```
57 -
48 #### Context
49
50 ```js
packages/react-is/src/__tests__/ReactIs-test.js
-21
@@ -58,9 +58,6 @@ describe('ReactIs', () => {
58 true,
59 );
60 expect(ReactIs.isValidElementType(React.Fragment)).toEqual(true);
61 - expect(ReactIs.isValidElementType(React.unstable_ConcurrentMode)).toEqual(
62 - true,
63 - );
61 expect(ReactIs.isValidElementType(React.StrictMode)).toEqual(true);
62 expect(ReactIs.isValidElementType(React.Suspense)).toEqual(true);
63
@@ -72,20 +69,6 @@ describe('ReactIs', () => {
69 expect(ReactIs.isValidElementType({type: 'div', props: {}})).toEqual(false);
70 });
71
75 - it('should identify concurrent mode', () => {
76 - expect(ReactIs.typeOf(<React.unstable_ConcurrentMode />)).toBe(
77 - ReactIs.ConcurrentMode,
78 - );
79 - expect(ReactIs.isConcurrentMode(<React.unstable_ConcurrentMode />)).toBe(
80 - true,
81 - );
82 - expect(ReactIs.isConcurrentMode({type: ReactIs.ConcurrentMode})).toBe(
83 - false,
84 - );
85 - expect(ReactIs.isConcurrentMode(<React.StrictMode />)).toBe(false);
86 - expect(ReactIs.isConcurrentMode(<div />)).toBe(false);
87 - });
88 -
72 it('should identify context consumers', () => {
73 const Context = React.createContext(false);
74 expect(ReactIs.typeOf(<Context.Consumer />)).toBe(ReactIs.ContextConsumer);
@@ -117,7 +100,6 @@ describe('ReactIs', () => {
100 expect(ReactIs.isElement(<Context.Provider />)).toBe(true);
101 expect(ReactIs.isElement(<Context.Consumer />)).toBe(true);
102 expect(ReactIs.isElement(<React.Fragment />)).toBe(true);
120 - expect(ReactIs.isElement(<React.unstable_ConcurrentMode />)).toBe(true);
103 expect(ReactIs.isElement(<React.StrictMode />)).toBe(true);
104 expect(ReactIs.isElement(<React.Suspense />)).toBe(true);
105 });
@@ -127,7 +109,6 @@ describe('ReactIs', () => {
109 expect(ReactIs.typeOf(<RefForwardingComponent />)).toBe(ReactIs.ForwardRef);
110 expect(ReactIs.isForwardRef(<RefForwardingComponent />)).toBe(true);
111 expect(ReactIs.isForwardRef({type: ReactIs.StrictMode})).toBe(false);
130 - expect(ReactIs.isForwardRef(<React.unstable_ConcurrentMode />)).toBe(false);
112 expect(ReactIs.isForwardRef(<div />)).toBe(false);
113 });
114
@@ -168,7 +149,6 @@ describe('ReactIs', () => {
149 expect(ReactIs.typeOf(<React.StrictMode />)).toBe(ReactIs.StrictMode);
150 expect(ReactIs.isStrictMode(<React.StrictMode />)).toBe(true);
151 expect(ReactIs.isStrictMode({type: ReactIs.StrictMode})).toBe(false);
171 - expect(ReactIs.isStrictMode(<React.unstable_ConcurrentMode />)).toBe(false);
152 expect(ReactIs.isStrictMode(<div />)).toBe(false);
153 });
154
@@ -188,7 +168,6 @@ describe('ReactIs', () => {
168 ReactIs.isProfiler(<React.Profiler id="foo" onRender={jest.fn()} />),
169 ).toBe(true);
170 expect(ReactIs.isProfiler({type: ReactIs.Profiler})).toBe(false);
191 - expect(ReactIs.isProfiler(<React.unstable_ConcurrentMode />)).toBe(false);
171 expect(ReactIs.isProfiler(<div />)).toBe(false);
172 });
173 });
packages/react-reconciler/src/__tests__/ReactIncrementalPerf-test.internal.js
+2 -4
@@ -189,14 +189,12 @@ describe('ReactDebugFiberPerf', () => {
189 expect(getFlameChart()).toMatchSnapshot();
190 });
191
192 - it('does not include ConcurrentMode, StrictMode, or Profiler components in measurements', () => {
192 + it('does not include StrictMode or Profiler components in measurements', () => {
193 ReactNoop.render(
194 <React.Profiler id="test" onRender={jest.fn()}>
195 <React.StrictMode>
196 <Parent>
197 - <React.unstable_ConcurrentMode>
198 - <Child />
199 - </React.unstable_ConcurrentMode>
197 + <Child />
198 </Parent>
199 </React.StrictMode>
200 </React.Profiler>,
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js
+7 -277
@@ -5,8 +5,6 @@ let ReactNoop;
5 let Scheduler;
6 let ReactCache;
7 let Suspense;
8 -let StrictMode;
9 -let ConcurrentMode;
8
9 let TextResource;
10 let textResourceShouldFail;
@@ -26,8 +24,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
24 Scheduler = require('scheduler');
25 ReactCache = require('react-cache');
26 Suspense = React.Suspense;
29 - StrictMode = React.StrictMode;
30 - ConcurrentMode = React.unstable_ConcurrentMode;
27
28 TextResource = ReactCache.unstable_createResource(([text, ms = 0]) => {
29 return new Promise((resolve, reject) =>
@@ -896,7 +892,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
892 expect(ReactNoop.getChildren()).toEqual([span('Result')]);
893 });
894
899 - it('times out immediately when Suspense is in loose mode, even if the suspender is async', async () => {
895 + it('times out immediately when Suspense is in legacy mode', async () => {
896 class UpdatingText extends React.Component {
897 state = {step: 1};
898 render() {
@@ -918,15 +914,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
914 function App() {
915 return (
916 <Suspense fallback={<Spinner />}>
921 - <ConcurrentMode>
922 - <UpdatingText ref={text} />
923 - <Text text="Sibling" />
924 - </ConcurrentMode>
917 + <UpdatingText ref={text} />
918 + <Text text="Sibling" />
919 </Suspense>
920 );
921 }
922
929 - // Initial mount. This is synchronous, because the root is sync.
923 + // Initial mount.
924 ReactNoop.renderLegacySyncRoot(<App />);
925 await advanceTimers(100);
926 expect(Scheduler).toHaveYielded([
@@ -945,14 +939,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
939 </React.Fragment>,
940 );
941
948 - // Update. This starts out asynchronously.
942 + // Update.
943 text.current.setState({step: 2}, () =>
944 Scheduler.yieldValue('Update did commit'),
945 );
946
953 - // Suspend during an async render.
954 - expect(ReactNoop.flushNextYield()).toEqual(['Suspend! [Step: 2]']);
955 - expect(Scheduler).toFlushAndYield([
947 + expect(ReactNoop.flushNextYield()).toEqual([
948 + 'Suspend! [Step: 2]',
949 'Loading (1)',
950 'Loading (2)',
951 'Loading (3)',
@@ -979,269 +972,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
972 );
973 });
974
982 - it(
983 - 'continues rendering asynchronously even if a promise is captured by ' +
984 - 'a sync boundary (legacy mode)',
985 - async () => {
986 - class UpdatingText extends React.Component {
987 - state = {text: this.props.initialText};
988 - render() {
989 - return this.props.children(this.state.text);
990 - }
991 - }
992 -
993 - const text1 = React.createRef(null);
994 - const text2 = React.createRef(null);
995 - function App() {
996 - return (
997 - <Fragment>
998 - <Suspense fallback={<Text text="Loading..." />}>
999 - <ConcurrentMode>
1000 - <UpdatingText ref={text1} initialText="Async: 1">
1001 - {text => (
1002 - <Fragment>
1003 - <Text text="Before" />
1004 - <AsyncText text={text} />
1005 - <Text text="After" />
1006 - </Fragment>
1007 - )}
1008 - </UpdatingText>
1009 - </ConcurrentMode>
1010 - </Suspense>
1011 - <ConcurrentMode>
1012 - <UpdatingText ref={text2} initialText="Sync: 1">
1013 - {text => (
1014 - <Fragment>
1015 - <Text text="Before" />
1016 - <Text text={text} />
1017 - <Text text="After" />
1018 - </Fragment>
1019 - )}
1020 - </UpdatingText>
1021 - </ConcurrentMode>
1022 - </Fragment>
1023 - );
1024 - }
1025 -
1026 - // Initial mount
1027 - ReactNoop.renderLegacySyncRoot(<App />, () =>
1028 - Scheduler.yieldValue('Did mount'),
1029 - );
1030 - await advanceTimers(100);
1031 -
1032 - expect(Scheduler).toHaveYielded([
1033 - 'Before',
1034 - 'Suspend! [Async: 1]',
1035 - 'After',
1036 - 'Loading...',
1037 - 'Before',
1038 - 'Sync: 1',
1039 - 'After',
1040 - 'Did mount',
1041 - 'Promise resolved [Async: 1]',
1042 - ]);
1043 - expect(Scheduler).toFlushExpired(['Async: 1']);
1044 - expect(ReactNoop).toMatchRenderedOutput(
1045 - <React.Fragment>
1046 - <span prop="Before" />
1047 - <span prop="Async: 1" />
1048 - <span prop="After" />
1049 -
1050 - <span prop="Before" />
1051 - <span prop="Sync: 1" />
1052 - <span prop="After" />
1053 - </React.Fragment>,
1054 - );
1055 -
1056 - // Update. This starts out asynchronously.
1057 - text1.current.setState({text: 'Async: 2'}, () =>
1058 - Scheduler.yieldValue('Update 1 did commit'),
1059 - );
1060 - text2.current.setState({text: 'Sync: 2'}, () =>
1061 - Scheduler.yieldValue('Update 2 did commit'),
1062 - );
1063 -
1064 - // Start rendering asynchronously
1065 - expect(Scheduler).toFlushAndYieldThrough(['Before']);
1066 -
1067 - // Now render the next child, which suspends
1068 - expect(ReactNoop.flushNextYield()).toEqual([
1069 - // This child suspends
1070 - 'Suspend! [Async: 2]',
1071 - ]);
1072 - expect(Scheduler).toFlushAndYield([
1073 - 'After',
1074 - 'Loading...',
1075 - 'Before',
1076 - 'Sync: 2',
1077 - 'After',
1078 - 'Update 1 did commit',
1079 - 'Update 2 did commit',
1080 - ]);
1081 - expect(ReactNoop).toMatchRenderedOutput(
1082 - <React.Fragment>
1083 - <span hidden={true} prop="Before" />
1084 - <span hidden={true} prop="Async: 1" />
1085 - <span hidden={true} prop="After" />
1086 - <span prop="Loading..." />
1087 -
1088 - <span prop="Before" />
1089 - <span prop="Sync: 2" />
1090 - <span prop="After" />
1091 - </React.Fragment>,
1092 - );
1093 -
1094 - // When the placeholder is pinged, the boundary must be re-rendered
1095 - // synchronously.
1096 - await advanceTimers(100);
1097 -
1098 - expect(Scheduler).toHaveYielded(['Promise resolved [Async: 2]']);
1099 - expect(Scheduler).toFlushExpired(['Async: 2']);
1100 - expect(ReactNoop).toMatchRenderedOutput(
1101 - <React.Fragment>
1102 - <span prop="Before" />
1103 - <span prop="Async: 2" />
1104 - <span prop="After" />
1105 -
1106 - <span prop="Before" />
1107 - <span prop="Sync: 2" />
1108 - <span prop="After" />
1109 - </React.Fragment>,
1110 - );
1111 - },
1112 - );
1113 -
1114 - it(
1115 - 'continues rendering asynchronously even if a promise is captured by ' +
1116 - 'a sync boundary (strict, legacy)',
1117 - async () => {
1118 - class UpdatingText extends React.Component {
1119 - state = {text: this.props.initialText};
1120 - render() {
1121 - return this.props.children(this.state.text);
1122 - }
1123 - }
1124 -
1125 - const text1 = React.createRef(null);
1126 - const text2 = React.createRef(null);
1127 - function App() {
1128 - return (
1129 - <StrictMode>
1130 - <Suspense fallback={<Text text="Loading..." />}>
1131 - <ConcurrentMode>
1132 - <UpdatingText ref={text1} initialText="Async: 1">
1133 - {text => (
1134 - <Fragment>
1135 - <Text text="Before" />
1136 - <AsyncText text={text} />
1137 - <Text text="After" />
1138 - </Fragment>
1139 - )}
1140 - </UpdatingText>
1141 - </ConcurrentMode>
1142 - </Suspense>
1143 - <ConcurrentMode>
1144 - <UpdatingText ref={text2} initialText="Sync: 1">
1145 - {text => (
1146 - <Fragment>
1147 - <Text text="Before" />
1148 - <Text text={text} />
1149 - <Text text="After" />
1150 - </Fragment>
1151 - )}
1152 - </UpdatingText>
1153 - </ConcurrentMode>
1154 - </StrictMode>
1155 - );
1156 - }
1157 -
1158 - // Initial mount
1159 - ReactNoop.renderLegacySyncRoot(<App />, () =>
1160 - Scheduler.yieldValue('Did mount'),
1161 - );
1162 - await advanceTimers(100);
1163 - expect(Scheduler).toHaveYielded([
1164 - 'Before',
1165 - 'Suspend! [Async: 1]',
1166 - 'After',
1167 - 'Loading...',
1168 - 'Before',
1169 - 'Sync: 1',
1170 - 'After',
1171 - 'Did mount',
1172 - 'Promise resolved [Async: 1]',
1173 - ]);
1174 - expect(Scheduler).toFlushExpired(['Async: 1']);
1175 - expect(ReactNoop).toMatchRenderedOutput(
1176 - <React.Fragment>
1177 - <span prop="Before" />
1178 - <span prop="Async: 1" />
1179 - <span prop="After" />
1180 -
1181 - <span prop="Before" />
1182 - <span prop="Sync: 1" />
1183 - <span prop="After" />
1184 - </React.Fragment>,
1185 - );
1186 -
1187 - // Update. This starts out asynchronously.
1188 - text1.current.setState({text: 'Async: 2'}, () =>
1189 - Scheduler.yieldValue('Update 1 did commit'),
1190 - );
1191 - text2.current.setState({text: 'Sync: 2'}, () =>
1192 - Scheduler.yieldValue('Update 2 did commit'),
1193 - );
1194 -
1195 - // Start rendering asynchronously
1196 - expect(Scheduler).toFlushAndYieldThrough(['Before']);
1197 -
1198 - // Now render the next child, which suspends
1199 - expect(ReactNoop.flushNextYield()).toEqual([
1200 - // This child suspends
1201 - 'Suspend! [Async: 2]',
1202 - ]);
1203 - expect(Scheduler).toFlushAndYield([
1204 - 'After',
1205 - 'Loading...',
1206 - 'Before',
1207 - 'Sync: 2',
1208 - 'After',
1209 - 'Update 1 did commit',
1210 - 'Update 2 did commit',
1211 - ]);
1212 - expect(ReactNoop).toMatchRenderedOutput(
1213 - <React.Fragment>
1214 - <span hidden={true} prop="Before" />
1215 - <span hidden={true} prop="Async: 1" />
1216 - <span hidden={true} prop="After" />
1217 - <span prop="Loading..." />
1218 -
1219 - <span prop="Before" />
1220 - <span prop="Sync: 2" />
1221 - <span prop="After" />
1222 - </React.Fragment>,
1223 - );
1224 -
1225 - // When the placeholder is pinged, the boundary must be re-rendered
1226 - // synchronously.
1227 - await advanceTimers(100);
1228 -
1229 - expect(Scheduler).toHaveYielded(['Promise resolved [Async: 2]']);
1230 - expect(Scheduler).toFlushExpired(['Async: 2']);
1231 - expect(ReactNoop).toMatchRenderedOutput(
1232 - <React.Fragment>
1233 - <span prop="Before" />
1234 - <span prop="Async: 2" />
1235 - <span prop="After" />
1236 -
1237 - <span prop="Before" />
1238 - <span prop="Sync: 2" />
1239 - <span prop="After" />
1240 - </React.Fragment>,
1241 - );
1242 - },
1243 - );
1244 -
975 it('does not re-render siblings in loose mode', async () => {
976 class TextWithLifecycle extends React.Component {
977 componentDidMount() {
packages/react-reconciler/src/__tests__/__snapshots__/ReactIncrementalPerf-test.internal.js.snap
+1 -1
@@ -91,7 +91,7 @@ exports[`ReactDebugFiberPerf deduplicates lifecycle names during commit to reduc
91 "
92 `;
93
94 -exports[`ReactDebugFiberPerf does not include ConcurrentMode, StrictMode, or Profiler components in measurements 1`] = `
94 +exports[`ReactDebugFiberPerf does not include StrictMode or Profiler components in measurements 1`] = `
95 "⚛ (Waiting for async callback...)
96
97 // Mount
packages/react/src/React.js
+1 -14
@@ -7,7 +7,6 @@
7
8 import ReactVersion from 'shared/ReactVersion';
9 import {
10 - REACT_CONCURRENT_MODE_TYPE,
10 REACT_FRAGMENT_TYPE,
11 REACT_PROFILER_TYPE,
12 REACT_STRICT_MODE_TYPE,
@@ -51,12 +50,7 @@ import {
50 } from './ReactElementValidator';
51 import ReactSharedInternals from './ReactSharedInternals';
52 import {error, warn} from './withComponentStack';
54 -import {
55 - enableEventAPI,
56 - enableStableConcurrentModeAPIs,
57 - enableJSXTransformAPI,
58 -} from 'shared/ReactFeatureFlags';
59 -
53 +import {enableEventAPI, enableJSXTransformAPI} from 'shared/ReactFeatureFlags';
54 const React = {
55 Children: {
56 map,
@@ -101,8 +95,6 @@ const React = {
95
96 version: ReactVersion,
97
104 - unstable_ConcurrentMode: REACT_CONCURRENT_MODE_TYPE,
105 -
98 __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
99 };
100
@@ -115,11 +107,6 @@ if (enableEventAPI) {
107 React.unstable_createEventComponent = createEventComponent;
108 }
109
118 -if (enableStableConcurrentModeAPIs) {
119 - React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
120 - React.unstable_ConcurrentMode = undefined;
121 -}
122 -
110 if (enableJSXTransformAPI) {
111 if (__DEV__) {
112 React.jsxDEV = jsxWithValidation;
packages/react/src/__tests__/ReactStrictMode-test.internal.js
+36 -143
@@ -10,6 +10,7 @@
10 'use strict';
11
12 let React;
13 +let Scheduler;
14 let ReactFeatureFlags;
15 let ReactTestRenderer;
16 let PropTypes;
@@ -21,6 +22,7 @@ describe('ReactStrictMode', () => {
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
@@ -153,6 +155,7 @@ describe('ReactStrictMode', () => {
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
@@ -298,27 +301,16 @@ describe('ReactStrictMode', () => {
301 });
302 });
303
301 - describe('async subtree', () => {
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
309 - it('should warn about unsafe legacy lifecycle methods within the tree', () => {
310 - class SyncRoot extends React.Component {
311 - UNSAFE_componentWillMount() {}
312 - UNSAFE_componentWillUpdate() {}
313 - UNSAFE_componentWillReceiveProps() {}
314 - render() {
315 - return (
316 - <React.unstable_ConcurrentMode>
317 - <AsyncRoot />
318 - </React.unstable_ConcurrentMode>
319 - );
320 - }
321 - }
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() {}
@@ -352,13 +344,12 @@ describe('ReactStrictMode', () => {
344 }
345 }
346
355 - let rendered;
356 - expect(() => {
357 - rendered = ReactTestRenderer.create(<SyncRoot />);
358 - }).toWarnDev(
347 + const root = ReactTestRenderer.create(null, {
348 + unstable_isConcurrent: true,
349 + });
350 + root.update(<AsyncRoot />);
351 + expect(() => Scheduler.flushAll()).toWarnDev(
352 'Unsafe lifecycle methods were found within a strict-mode tree:' +
360 - '\n in ConcurrentMode (at **)' +
361 - '\n in SyncRoot (at **)' +
353 '\n\ncomponentWillMount: Please update the following components ' +
354 'to use componentDidMount instead: AsyncRoot' +
355 '\n\ncomponentWillReceiveProps: Please update the following components ' +
@@ -367,26 +358,15 @@ describe('ReactStrictMode', () => {
358 'to use componentDidUpdate instead: AsyncRoot' +
359 '\n\nLearn more about this warning here:' +
360 '\nhttps://fb.me/react-strict-mode-warnings',
361 + {withoutStack: true},
362 );
363
364 // Dedupe
373 - rendered = ReactTestRenderer.create(<SyncRoot />);
374 - rendered.update(<SyncRoot />);
365 + root.update(<AsyncRoot />);
366 + Scheduler.flushAll();
367 });
368
369 it('should coalesce warnings by lifecycle name', () => {
378 - class SyncRoot extends React.Component {
379 - UNSAFE_componentWillMount() {}
380 - UNSAFE_componentWillUpdate() {}
381 - UNSAFE_componentWillReceiveProps() {}
382 - render() {
383 - return (
384 - <React.unstable_ConcurrentMode>
385 - <AsyncRoot />
386 - </React.unstable_ConcurrentMode>
387 - );
388 - }
389 - }
370 class AsyncRoot extends React.Component {
371 UNSAFE_componentWillMount() {}
372 UNSAFE_componentWillUpdate() {}
@@ -409,15 +389,14 @@ describe('ReactStrictMode', () => {
389 }
390 }
391
412 - let rendered;
392 + const root = ReactTestRenderer.create(null, {
393 + unstable_isConcurrent: true,
394 + });
395 + root.update(<AsyncRoot />);
396
397 expect(() => {
415 - expect(
416 - () => (rendered = ReactTestRenderer.create(<SyncRoot />)),
417 - ).toWarnDev(
398 + expect(() => Scheduler.flushAll()).toWarnDev(
399 'Unsafe lifecycle methods were found within a strict-mode tree:' +
419 - '\n in ConcurrentMode (at **)' +
420 - '\n in SyncRoot (at **)' +
400 '\n\ncomponentWillMount: Please update the following components ' +
401 'to use componentDidMount instead: AsyncRoot, Parent' +
402 '\n\ncomponentWillReceiveProps: Please update the following components ' +
@@ -426,6 +405,7 @@ describe('ReactStrictMode', () => {
405 'to use componentDidUpdate instead: AsyncRoot, Parent' +
406 '\n\nLearn more about this warning here:' +
407 '\nhttps://fb.me/react-strict-mode-warnings',
408 + {withoutStack: true},
409 );
410 }).toLowPriorityWarnDev(
411 [
@@ -437,103 +417,14 @@ describe('ReactStrictMode', () => {
417 );
418
419 // Dedupe
440 - rendered = ReactTestRenderer.create(<SyncRoot />);
441 - rendered.update(<SyncRoot />);
442 - });
443 -
444 - it('should group warnings by async root', () => {
445 - class SyncRoot extends React.Component {
446 - UNSAFE_componentWillMount() {}
447 - UNSAFE_componentWillUpdate() {}
448 - UNSAFE_componentWillReceiveProps() {}
449 - render() {
450 - return (
451 - <div>
452 - <AsyncRootOne />
453 - <AsyncRootTwo />
454 - </div>
455 - );
456 - }
457 - }
458 - class AsyncRootOne extends React.Component {
459 - render() {
460 - return (
461 - <React.unstable_ConcurrentMode>
462 - <Foo>
463 - <Bar />
464 - </Foo>
465 - </React.unstable_ConcurrentMode>
466 - );
467 - }
468 - }
469 - class AsyncRootTwo extends React.Component {
470 - render() {
471 - return (
472 - <React.unstable_ConcurrentMode>
473 - <Foo>
474 - <Baz />
475 - </Foo>
476 - </React.unstable_ConcurrentMode>
477 - );
478 - }
479 - }
480 - class Foo extends React.Component {
481 - componentWillMount() {}
482 - render() {
483 - return this.props.children;
484 - }
485 - }
486 - class Bar extends React.Component {
487 - componentWillMount() {}
488 - render() {
489 - return null;
490 - }
491 - }
492 - class Baz extends React.Component {
493 - componentWillMount() {}
494 - render() {
495 - return null;
496 - }
497 - }
498 -
499 - let rendered;
500 -
501 - expect(() => {
502 - expect(
503 - () => (rendered = ReactTestRenderer.create(<SyncRoot />)),
504 - ).toWarnDev([
505 - 'Unsafe lifecycle methods were found within a strict-mode tree:' +
506 - '\n in ConcurrentMode (at **)' +
507 - '\n in AsyncRootOne (at **)' +
508 - '\n in div (at **)' +
509 - '\n in SyncRoot (at **)' +
510 - '\n\ncomponentWillMount: Please update the following components ' +
511 - 'to use componentDidMount instead: Bar, Foo',
512 - 'Unsafe lifecycle methods were found within a strict-mode tree:' +
513 - '\n in ConcurrentMode (at **)' +
514 - '\n in AsyncRootTwo (at **)' +
515 - '\n in div (at **)' +
516 - '\n in SyncRoot (at **)' +
517 - '\n\ncomponentWillMount: Please update the following components ' +
518 - 'to use componentDidMount instead: Baz',
519 - ]);
520 - }).toLowPriorityWarnDev(['componentWillMount is deprecated'], {
521 - withoutStack: true,
522 - });
523 -
524 - // Dedupe
525 - rendered = ReactTestRenderer.create(<SyncRoot />);
526 - rendered.update(<SyncRoot />);
420 + root.update(<AsyncRoot />);
421 + Scheduler.flushAll();
422 });
423
424 it('should warn about components not present during the initial render', () => {
425 class AsyncRoot extends React.Component {
426 render() {
532 - return (
533 - <React.unstable_ConcurrentMode>
534 - {this.props.foo ? <Foo /> : <Bar />}
535 - </React.unstable_ConcurrentMode>
536 - );
427 + return this.props.foo ? <Foo /> : <Bar />;
428 }
429 }
430 class Foo extends React.Component {
@@ -549,32 +440,34 @@ describe('ReactStrictMode', () => {
440 }
441 }
442
552 - let rendered;
553 - expect(() => {
554 - rendered = ReactTestRenderer.create(<AsyncRoot foo={true} />);
555 - }).toWarnDev(
443 + const root = ReactTestRenderer.create(null, {
444 + unstable_isConcurrent: true,
445 + });
446 + root.update(<AsyncRoot foo={true} />);
447 + expect(() => Scheduler.flushAll()).toWarnDev(
448 'Unsafe lifecycle methods were found within a strict-mode tree:' +
557 - '\n in ConcurrentMode (at **)' +
558 - '\n in AsyncRoot (at **)' +
449 '\n\ncomponentWillMount: Please update the following components ' +
450 'to use componentDidMount instead: Foo' +
451 '\n\nLearn more about this warning here:' +
452 '\nhttps://fb.me/react-strict-mode-warnings',
453 + {withoutStack: true},
454 );
455
565 - expect(() => rendered.update(<AsyncRoot foo={false} />)).toWarnDev(
456 + root.update(<AsyncRoot foo={false} />);
457 + expect(() => Scheduler.flushAll()).toWarnDev(
458 'Unsafe lifecycle methods were found within a strict-mode tree:' +
567 - '\n in ConcurrentMode (at **)' +
568 - '\n in AsyncRoot (at **)' +
459 '\n\ncomponentWillMount: Please update the following components ' +
460 'to use componentDidMount instead: Bar' +
461 '\n\nLearn more about this warning here:' +
462 '\nhttps://fb.me/react-strict-mode-warnings',
463 + {withoutStack: true},
464 );
465
466 // Dedupe
576 - rendered.update(<AsyncRoot foo={true} />);
577 - rendered.update(<AsyncRoot foo={false} />);
467 + root.update(<AsyncRoot foo={true} />);
468 + Scheduler.flushAll();
469 + root.update(<AsyncRoot foo={false} />);
470 + Scheduler.flushAll();
471 });
472
473 it('should also warn inside of "strict" mode trees', () => {
packages/shared/ReactSymbols.js
+2
@@ -32,6 +32,8 @@ export const REACT_PROVIDER_TYPE = hasSymbol
32 export const REACT_CONTEXT_TYPE = hasSymbol
33 ? Symbol.for('react.context')
34 : 0xeace;
35 +// TODO: We don't use AsyncMode or ConcurrentMode anymore. They were temporary
36 +// (unstable) APIs that have been removed. Can we remove the symbols?
37 export const REACT_ASYNC_MODE_TYPE = hasSymbol
38 ? Symbol.for('react.async_mode')
39 : 0xeacf;
packages/shared/getComponentName.js
-3
@@ -11,7 +11,6 @@ import type {LazyComponent} from 'shared/ReactLazyComponent';
11
12 import warningWithoutStack from 'shared/warningWithoutStack';
13 import {
14 - REACT_CONCURRENT_MODE_TYPE,
14 REACT_CONTEXT_TYPE,
15 REACT_FORWARD_REF_TYPE,
16 REACT_FRAGMENT_TYPE,
@@ -64,8 +63,6 @@ function getComponentName(type: mixed): string | null {
63 return type;
64 }
65 switch (type) {
67 - case REACT_CONCURRENT_MODE_TYPE:
68 - return 'ConcurrentMode';
66 case REACT_FRAGMENT_TYPE:
67 return 'Fragment';
68 case REACT_PORTAL_TYPE: