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.
This commit is contained in:
Andrew Clark 2017-04-18 16:38:55 -07:00
parent 0c896ffe6b
commit 9b676a7d0b
8 changed files with 71 additions and 59 deletions

View File

@ -751,11 +751,11 @@ src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
* renders synchronously by default * renders synchronously by default
* throws when calling async APIs when feature flag is disabled * renders syncrhonously when feature flag is disabled
* unstable_asyncUpdates creates an async subtree
* updates inside an async subtree are async by default
* unstable_asyncUpdates at the root makes the entire tree async * unstable_asyncUpdates at the root makes the entire tree async
* updates inside an async tree are async by default * updates inside an async tree are async by default
* unstable_asyncUpdates creates an async subtree
* updates inside an async subtree are async by default
src/renderers/dom/shared/__tests__/CSSProperty-test.js src/renderers/dom/shared/__tests__/CSSProperty-test.js
* should generate browser prefixes for its `isUnitlessNumber` * should generate browser prefixes for its `isUnitlessNumber`

View File

@ -364,7 +364,6 @@ var DOMRenderer = ReactFiberReconciler({
scheduleDeferredCallback: ReactDOMFrameScheduling.rIC, scheduleDeferredCallback: ReactDOMFrameScheduling.rIC,
useSyncScheduling: !ReactDOMFeatureFlags.fiberAsyncScheduling, useSyncScheduling: !ReactDOMFeatureFlags.fiberAsyncScheduling,
enableAsyncSubtreeAPI: ReactDOMFeatureFlags.enableAsyncSubtreeAPI,
}); });
ReactGenericBatching.injection.injectFiberBatchedUpdates( ReactGenericBatching.injection.injectFiberBatchedUpdates(

View File

@ -1,5 +1,6 @@
var React = require('react'); var React = require('react');
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ReactFeatureFlags = require('ReactFeatureFlags');
var ReactDOM; var ReactDOM;
@ -23,21 +24,69 @@ describe('ReactDOMFiberAsync', () => {
}); });
if (ReactDOMFeatureFlags.useFiber) { if (ReactDOMFeatureFlags.useFiber) {
it('throws when calling async APIs when feature flag is disabled', () => { it('renders syncrhonously when feature flag is disabled', () => {
expect(() => { function Async(props) {
ReactDOM.unstable_asyncRender(<div>Hi</div>, container); return props.children;
}).toThrow('ReactDOM.unstable_asyncRender is not a function'); }
Async.unstable_asyncUpdates = true;
ReactDOM.render(<Async><div>Hi</div></Async>, container);
expect(container.textContent).toEqual('Hi');
ReactDOM.render(<Async><div>Bye</div></Async>, container);
expect(container.textContent).toEqual('Bye');
}); });
describe('with feature flag enabled', () => { describe('with feature flag enabled', () => {
beforeEach(() => { beforeEach(() => {
jest.resetModules(); jest.resetModules();
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); ReactFeatureFlags = require('ReactFeatureFlags');
container = document.createElement('div'); container = document.createElement('div');
ReactDOMFeatureFlags.enableAsyncSubtreeAPI = true; ReactFeatureFlags.enableAsyncSubtreeAPI = true;
ReactDOM = require('react-dom'); ReactDOM = require('react-dom');
}); });
it('unstable_asyncUpdates at the root makes the entire tree async', () => {
function Async(props) {
return props.children;
}
Async.unstable_asyncUpdates = true;
ReactDOM.render(<Async><div>Hi</div></Async>, container);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('Hi');
ReactDOM.render(<Async><div>Bye</div></Async>, container);
expect(container.textContent).toEqual('Hi');
jest.runAllTimers();
expect(container.textContent).toEqual('Bye');
});
it('updates inside an async tree are async by default', () => {
function Async(props) {
return props.children;
}
Async.unstable_asyncUpdates = true;
let instance;
class Component extends React.Component {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
}
ReactDOM.render(<Async><Component /></Async>, container);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('0');
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
it('unstable_asyncUpdates creates an async subtree', () => { it('unstable_asyncUpdates creates an async subtree', () => {
let instance; let instance;
class Component extends React.Component { class Component extends React.Component {
@ -81,48 +130,6 @@ describe('ReactDOMFiberAsync', () => {
jest.runAllTimers(); jest.runAllTimers();
expect(container.textContent).toEqual('1'); expect(container.textContent).toEqual('1');
}); });
it('unstable_asyncUpdates at the root makes the entire tree async', () => {
function Async(props) {
return props.children;
}
Async.unstable_asyncUpdates = true;
ReactDOM.render(<Async><div>Hi</div></Async>, container);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('Hi');
ReactDOM.render(<Async><div>Bye</div></Async>, container);
expect(container.textContent).toEqual('Hi');
jest.runAllTimers();
expect(container.textContent).toEqual('Bye');
});
it('updates inside an async tree are async by default', () => {
function Async(props) {
return props.children;
}
Async.unstable_asyncUpdates = true;
let instance;
class Component extends React.Component {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
}
ReactDOM.render(<Async><Component /></Async>, container);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('0');
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
}); });
} }
}); });

View File

@ -13,7 +13,6 @@
var ReactDOMFeatureFlags = { var ReactDOMFeatureFlags = {
fiberAsyncScheduling: false, fiberAsyncScheduling: false,
enableAsyncSubtreeAPI: false,
useCreateElement: true, useCreateElement: true,
useFiber: true, useFiber: true,
}; };

View File

@ -23,6 +23,8 @@ import type {TypeOfSideEffect} from 'ReactTypeOfSideEffect';
import type {PriorityLevel} from 'ReactPriorityLevel'; import type {PriorityLevel} from 'ReactPriorityLevel';
import type {UpdateQueue} from 'ReactFiberUpdateQueue'; import type {UpdateQueue} from 'ReactFiberUpdateQueue';
var ReactFeatureFlags = require('ReactFeatureFlags');
var { var {
IndeterminateComponent, IndeterminateComponent,
ClassComponent, ClassComponent,
@ -374,7 +376,11 @@ function createFiberFromElementType(
internalContextTag: TypeOfInternalContext, internalContextTag: TypeOfInternalContext,
debugOwner: null | Fiber | ReactInstance, debugOwner: null | Fiber | ReactInstance,
): Fiber { ): Fiber {
if (type != null && (type: any).unstable_asyncUpdates === true) { if (
ReactFeatureFlags.enableAsyncSubtreeAPI &&
type != null &&
(type: any).unstable_asyncUpdates === true
) {
internalContextTag |= AsyncUpdates; internalContextTag |= AsyncUpdates;
} }

View File

@ -17,6 +17,8 @@ import type {FiberRoot} from 'ReactFiberRoot';
import type {PriorityLevel} from 'ReactPriorityLevel'; import type {PriorityLevel} from 'ReactPriorityLevel';
import type {ReactNodeList} from 'ReactTypes'; import type {ReactNodeList} from 'ReactTypes';
var ReactFeatureFlags = require('ReactFeatureFlags');
var { var {
AsyncUpdates, AsyncUpdates,
} = require('ReactTypeOfInternalContext'); } = require('ReactTypeOfInternalContext');
@ -119,7 +121,6 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
resetAfterCommit(): void, resetAfterCommit(): void,
useSyncScheduling?: boolean, useSyncScheduling?: boolean,
enableAsyncSubtreeAPI?: boolean,
}; };
export type Reconciler<C, I, TI> = { export type Reconciler<C, I, TI> = {
@ -189,7 +190,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
// Check if the top-level element is an async wrapper component. If so, treat // Check if the top-level element is an async wrapper component. If so, treat
// updates to the root as async. This is a bit weird but lets us avoid a separate // updates to the root as async. This is a bit weird but lets us avoid a separate
// `renderAsync` API. // `renderAsync` API.
const forceAsync = element != null && const forceAsync = ReactFeatureFlags.enableAsyncSubtreeAPI &&
element != null &&
element.type != null && element.type != null &&
(element.type: any).unstable_asyncUpdates === true; (element.type: any).unstable_asyncUpdates === true;
const priorityLevel = getPriorityContext(current, forceAsync); const priorityLevel = getPriorityContext(current, forceAsync);

View File

@ -169,7 +169,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
useSyncScheduling, useSyncScheduling,
prepareForCommit, prepareForCommit,
resetAfterCommit, resetAfterCommit,
enableAsyncSubtreeAPI,
} = config; } = config;
// The priority level to use when scheduling an update. We use NoWork to // 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>(
if (priorityLevel === NoWork) { if (priorityLevel === NoWork) {
if ( if (
!useSyncScheduling || !useSyncScheduling ||
(enableAsyncSubtreeAPI === true && fiber.internalContextTag & AsyncUpdates ||
fiber.internalContextTag & AsyncUpdates) ||
forceAsync forceAsync
) { ) {
priorityLevel = LowPriority; priorityLevel = LowPriority;

View File

@ -19,6 +19,7 @@ var ReactFeatureFlags = {
logTopLevelRenders: false, logTopLevelRenders: false,
prepareNewChildrenBeforeUnmountInStack: true, prepareNewChildrenBeforeUnmountInStack: true,
disableNewFiberFeatures: false, disableNewFiberFeatures: false,
enableAsyncSubtreeAPI: false,
}; };
module.exports = ReactFeatureFlags; module.exports = ReactFeatureFlags;