@samitouri / QOS-React / commits / 9b676a7d0b

Remove enableAsyncSubtreeAPI from host config

Use a ReactFeatureFlag instead. It won't be per-renderer, but we likely won't need that. When enableAsyncSubtreeAPI is false, unstable_asyncUpdates is ignored, but does not warn or throw. That way if we discover a bug in async mode, we can flip the flag and revert back to sync without code changes.

Andrew Clark committed Apr 18, 2017 at 16:38 UTC 9b676a7d0b2160d7301944a1c9d9caec9087e795
8 files changed +63 -51
scripts/fiber/tests-passing.txt
+3 -3
@@ -751,11 +751,11 @@ src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
751
752 src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
753 * renders synchronously by default
754 -* throws when calling async APIs when feature flag is disabled
755 -* unstable_asyncUpdates creates an async subtree
756 -* updates inside an async subtree are async by default
754 +* renders syncrhonously when feature flag is disabled
755 * unstable_asyncUpdates at the root makes the entire tree async
756 * updates inside an async tree are async by default
757 +* unstable_asyncUpdates creates an async subtree
758 +* updates inside an async subtree are async by default
759
760 src/renderers/dom/shared/__tests__/CSSProperty-test.js
761 * should generate browser prefixes for its `isUnitlessNumber`
src/renderers/dom/fiber/ReactDOMFiber.js
-1
@@ -364,7 +364,6 @@ var DOMRenderer = ReactFiberReconciler({
364 scheduleDeferredCallback: ReactDOMFrameScheduling.rIC,
365
366 useSyncScheduling: !ReactDOMFeatureFlags.fiberAsyncScheduling,
367 - enableAsyncSubtreeAPI: ReactDOMFeatureFlags.enableAsyncSubtreeAPI,
367 });
368
369 ReactGenericBatching.injection.injectFiberBatchedUpdates(
src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
+47 -40
@@ -1,5 +1,6 @@
1 var React = require('react');
2 var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
3 +var ReactFeatureFlags = require('ReactFeatureFlags');
4
5 var ReactDOM;
6
@@ -23,34 +24,62 @@ describe('ReactDOMFiberAsync', () => {
24 });
25
26 if (ReactDOMFeatureFlags.useFiber) {
26 - it('throws when calling async APIs when feature flag is disabled', () => {
27 - expect(() => {
28 - ReactDOM.unstable_asyncRender(<div>Hi</div>, container);
29 - }).toThrow('ReactDOM.unstable_asyncRender is not a function');
27 + it('renders syncrhonously when feature flag is disabled', () => {
28 + function Async(props) {
29 + return props.children;
30 + }
31 + Async.unstable_asyncUpdates = true;
32 + ReactDOM.render(<Async><div>Hi</div></Async>, container);
33 + expect(container.textContent).toEqual('Hi');
34 +
35 + ReactDOM.render(<Async><div>Bye</div></Async>, container);
36 + expect(container.textContent).toEqual('Bye');
37 });
38
39 describe('with feature flag enabled', () => {
40 beforeEach(() => {
41 jest.resetModules();
35 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
42 + ReactFeatureFlags = require('ReactFeatureFlags');
43 container = document.createElement('div');
37 - ReactDOMFeatureFlags.enableAsyncSubtreeAPI = true;
44 + ReactFeatureFlags.enableAsyncSubtreeAPI = true;
45 ReactDOM = require('react-dom');
46 });
47
41 - it('unstable_asyncUpdates creates an async subtree', () => {
48 + it('unstable_asyncUpdates at the root makes the entire tree async', () => {
49 + function Async(props) {
50 + return props.children;
51 + }
52 + Async.unstable_asyncUpdates = true;
53 + ReactDOM.render(<Async><div>Hi</div></Async>, container);
54 + expect(container.textContent).toEqual('');
55 + jest.runAllTimers();
56 + expect(container.textContent).toEqual('Hi');
57 +
58 + ReactDOM.render(<Async><div>Bye</div></Async>, container);
59 + expect(container.textContent).toEqual('Hi');
60 + jest.runAllTimers();
61 + expect(container.textContent).toEqual('Bye');
62 + });
63 +
64 + it('updates inside an async tree are async by default', () => {
65 + function Async(props) {
66 + return props.children;
67 + }
68 + Async.unstable_asyncUpdates = true;
69 +
70 let instance;
71 class Component extends React.Component {
72 state = {step: 0};
45 - static unstable_asyncUpdates = true;
73 render() {
74 instance = this;
75 return <div>{this.state.step}</div>;
76 }
77 }
78
52 - ReactDOM.render(<Component />, container);
79 + ReactDOM.render(<Async><Component /></Async>, container);
80 + expect(container.textContent).toEqual('');
81 jest.runAllTimers();
82 + expect(container.textContent).toEqual('0');
83
84 instance.setState({step: 1});
85 expect(container.textContent).toEqual('0');
@@ -58,15 +87,11 @@ describe('ReactDOMFiberAsync', () => {
87 expect(container.textContent).toEqual('1');
88 });
89
61 - it('updates inside an async subtree are async by default', () => {
62 - function Component(props) {
63 - return <Child />;
64 - }
65 - Component.unstable_asyncUpdates = true;
66 -
90 + it('unstable_asyncUpdates creates an async subtree', () => {
91 let instance;
68 - class Child extends React.Component {
92 + class Component extends React.Component {
93 state = {step: 0};
94 + static unstable_asyncUpdates = true;
95 render() {
96 instance = this;
97 return <div>{this.state.step}</div>;
@@ -82,30 +107,14 @@ describe('ReactDOMFiberAsync', () => {
107 expect(container.textContent).toEqual('1');
108 });
109
85 - it('unstable_asyncUpdates at the root makes the entire tree async', () => {
86 - function Async(props) {
87 - return props.children;
88 - }
89 - Async.unstable_asyncUpdates = true;
90 - ReactDOM.render(<Async><div>Hi</div></Async>, container);
91 - expect(container.textContent).toEqual('');
92 - jest.runAllTimers();
93 - expect(container.textContent).toEqual('Hi');
94 -
95 - ReactDOM.render(<Async><div>Bye</div></Async>, container);
96 - expect(container.textContent).toEqual('Hi');
97 - jest.runAllTimers();
98 - expect(container.textContent).toEqual('Bye');
99 - });
100 -
101 - it('updates inside an async tree are async by default', () => {
102 - function Async(props) {
103 - return props.children;
110 + it('updates inside an async subtree are async by default', () => {
111 + function Component(props) {
112 + return <Child />;
113 }
105 - Async.unstable_asyncUpdates = true;
114 + Component.unstable_asyncUpdates = true;
115
116 let instance;
108 - class Component extends React.Component {
117 + class Child extends React.Component {
118 state = {step: 0};
119 render() {
120 instance = this;
@@ -113,10 +122,8 @@ describe('ReactDOMFiberAsync', () => {
122 }
123 }
124
116 - ReactDOM.render(<Async><Component /></Async>, container);
117 - expect(container.textContent).toEqual('');
125 + ReactDOM.render(<Component />, container);
126 jest.runAllTimers();
119 - expect(container.textContent).toEqual('0');
127
128 instance.setState({step: 1});
129 expect(container.textContent).toEqual('0');
src/renderers/dom/shared/ReactDOMFeatureFlags.js
-1
@@ -13,7 +13,6 @@
13
14 var ReactDOMFeatureFlags = {
15 fiberAsyncScheduling: false,
16 - enableAsyncSubtreeAPI: false,
16 useCreateElement: true,
17 useFiber: true,
18 };
src/renderers/shared/fiber/ReactFiber.js
+7 -1
@@ -23,6 +23,8 @@ import type {TypeOfSideEffect} from 'ReactTypeOfSideEffect';
23 import type {PriorityLevel} from 'ReactPriorityLevel';
24 import type {UpdateQueue} from 'ReactFiberUpdateQueue';
25
26 +var ReactFeatureFlags = require('ReactFeatureFlags');
27 +
28 var {
29 IndeterminateComponent,
30 ClassComponent,
@@ -374,7 +376,11 @@ function createFiberFromElementType(
376 internalContextTag: TypeOfInternalContext,
377 debugOwner: null | Fiber | ReactInstance,
378 ): Fiber {
377 - if (type != null && (type: any).unstable_asyncUpdates === true) {
379 + if (
380 + ReactFeatureFlags.enableAsyncSubtreeAPI &&
381 + type != null &&
382 + (type: any).unstable_asyncUpdates === true
383 + ) {
384 internalContextTag |= AsyncUpdates;
385 }
386
src/renderers/shared/fiber/ReactFiberReconciler.js
+4 -2
@@ -17,6 +17,8 @@ import type {FiberRoot} from 'ReactFiberRoot';
17 import type {PriorityLevel} from 'ReactPriorityLevel';
18 import type {ReactNodeList} from 'ReactTypes';
19
20 +var ReactFeatureFlags = require('ReactFeatureFlags');
21 +
22 var {
23 AsyncUpdates,
24 } = require('ReactTypeOfInternalContext');
@@ -119,7 +121,6 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
121 resetAfterCommit(): void,
122
123 useSyncScheduling?: boolean,
122 - enableAsyncSubtreeAPI?: boolean,
124 };
125
126 export type Reconciler<C, I, TI> = {
@@ -189,7 +190,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
190 // Check if the top-level element is an async wrapper component. If so, treat
191 // updates to the root as async. This is a bit weird but lets us avoid a separate
192 // `renderAsync` API.
192 - const forceAsync = element != null &&
193 + const forceAsync = ReactFeatureFlags.enableAsyncSubtreeAPI &&
194 + element != null &&
195 element.type != null &&
196 (element.type: any).unstable_asyncUpdates === true;
197 const priorityLevel = getPriorityContext(current, forceAsync);
src/renderers/shared/fiber/ReactFiberScheduler.js
+1 -3
@@ -169,7 +169,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
169 useSyncScheduling,
170 prepareForCommit,
171 resetAfterCommit,
172 - enableAsyncSubtreeAPI,
172 } = config;
173
174 // The priority level to use when scheduling an update. We use NoWork to
@@ -1353,8 +1352,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
1352 if (priorityLevel === NoWork) {
1353 if (
1354 !useSyncScheduling ||
1356 - (enableAsyncSubtreeAPI === true &&
1357 - fiber.internalContextTag & AsyncUpdates) ||
1355 + fiber.internalContextTag & AsyncUpdates ||
1356 forceAsync
1357 ) {
1358 priorityLevel = LowPriority;
src/renderers/shared/utils/ReactFeatureFlags.js
+1
@@ -19,6 +19,7 @@ var ReactFeatureFlags = {
19 logTopLevelRenders: false,
20 prepareNewChildrenBeforeUnmountInStack: true,
21 disableNewFiberFeatures: false,
22 + enableAsyncSubtreeAPI: false,
23 };
24
25 module.exports = ReactFeatureFlags;