Skip to content

Commit

Permalink
React.unstable_AsyncComponent (#10239)
Browse files Browse the repository at this point in the history
Alternative to using the static class property unstable_asyncUpdates.
Everything inside <AsyncComponent /> has async updates by default. You
can also extend it like PureComponent or Component.
  • Loading branch information
acdlite authored Jul 21, 2017
1 parent 9d248e0 commit ae430b7
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 38 deletions.
4 changes: 2 additions & 2 deletions scripts/fiber/tests-passing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -674,9 +674,9 @@ src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
* renders synchronously by default
* renders synchronously when feature flag is disabled
* unstable_asyncUpdates at the root makes the entire tree async
* AsyncComponent at the root makes the entire tree async
* updates inside an async tree are async by default
* unstable_asyncUpdates creates an async subtree
* AsyncComponent creates an async subtree
* updates inside an async subtree are async by default

src/renderers/dom/shared/__tests__/CSSProperty-test.js
Expand Down
1 change: 1 addition & 0 deletions src/isomorphic/ReactEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var React = {

Component: ReactBaseClasses.Component,
PureComponent: ReactBaseClasses.PureComponent,
unstable_AsyncComponent: ReactBaseClasses.AsyncComponent,

createElement: createElement,
cloneElement: cloneElement,
Expand Down
28 changes: 24 additions & 4 deletions src/isomorphic/modern/class/ReactBaseClasses.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,33 @@ function ReactPureComponent(props, context, updater) {

function ComponentDummy() {}
ComponentDummy.prototype = ReactComponent.prototype;
ReactPureComponent.prototype = new ComponentDummy();
ReactPureComponent.prototype.constructor = ReactPureComponent;
var pureComponentPrototype = (ReactPureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = ReactPureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(ReactPureComponent.prototype, ReactComponent.prototype);
ReactPureComponent.prototype.isPureReactComponent = true;
Object.assign(pureComponentPrototype, ReactComponent.prototype);
pureComponentPrototype.isPureReactComponent = true;

function ReactAsyncComponent(props, context, updater) {
// Duplicated from ReactComponent.
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}

var asyncComponentPrototype = (ReactAsyncComponent.prototype = new ComponentDummy());
asyncComponentPrototype.constructor = ReactAsyncComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(asyncComponentPrototype, ReactComponent.prototype);
asyncComponentPrototype.unstable_isAsyncReactComponent = true;
asyncComponentPrototype.render = function() {
return this.props.children;
};

module.exports = {
Component: ReactComponent,
PureComponent: ReactPureComponent,
AsyncComponent: ReactAsyncComponent,
};
56 changes: 26 additions & 30 deletions src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ var ReactFeatureFlags = require('ReactFeatureFlags');

var ReactDOM;

var AsyncComponent = React.unstable_AsyncComponent;

describe('ReactDOMFiberAsync', () => {
var container;

Expand All @@ -25,16 +27,16 @@ describe('ReactDOMFiberAsync', () => {

if (ReactDOMFeatureFlags.useFiber) {
it('renders synchronously when feature flag is disabled', () => {
class Async extends React.Component {
static unstable_asyncUpdates = true;
render() {
return this.props.children;
}
}
ReactDOM.render(<Async><div>Hi</div></Async>, container);
ReactDOM.render(
<AsyncComponent><div>Hi</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Hi');

ReactDOM.render(<Async><div>Bye</div></Async>, container);
ReactDOM.render(
<AsyncComponent><div>Bye</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Bye');
});

Expand All @@ -47,32 +49,25 @@ describe('ReactDOMFiberAsync', () => {
ReactDOM = require('react-dom');
});

it('unstable_asyncUpdates at the root makes the entire tree async', () => {
class Async extends React.Component {
static unstable_asyncUpdates = true;
render() {
return this.props.children;
}
}
ReactDOM.render(<Async><div>Hi</div></Async>, container);
it('AsyncComponent at the root makes the entire tree async', () => {
ReactDOM.render(
<AsyncComponent><div>Hi</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('Hi');

ReactDOM.render(<Async><div>Bye</div></Async>, container);
ReactDOM.render(
<AsyncComponent><div>Bye</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Hi');
jest.runAllTimers();
expect(container.textContent).toEqual('Bye');
});

it('updates inside an async tree are async by default', () => {
class Async extends React.Component {
static unstable_asyncUpdates = true;
render() {
return this.props.children;
}
}

let instance;
class Component extends React.Component {
state = {step: 0};
Expand All @@ -82,7 +77,10 @@ describe('ReactDOMFiberAsync', () => {
}
}

ReactDOM.render(<Async><Component /></Async>, container);
ReactDOM.render(
<AsyncComponent><Component /></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('0');
Expand All @@ -93,11 +91,10 @@ describe('ReactDOMFiberAsync', () => {
expect(container.textContent).toEqual('1');
});

it('unstable_asyncUpdates creates an async subtree', () => {
it('AsyncComponent creates an async subtree', () => {
let instance;
class Component extends React.Component {
class Component extends React.unstable_AsyncComponent {
state = {step: 0};
static unstable_asyncUpdates = true;
render() {
instance = this;
return <div>{this.state.step}</div>;
Expand All @@ -114,8 +111,7 @@ describe('ReactDOMFiberAsync', () => {
});

it('updates inside an async subtree are async by default', () => {
class Component extends React.Component {
static unstable_asyncUpdates = true;
class Component extends React.unstable_AsyncComponent {
render() {
return <Child />;
}
Expand Down
3 changes: 2 additions & 1 deletion src/renderers/shared/fiber/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ module.exports = function(
if (
ReactFeatureFlags.enableAsyncSubtreeAPI &&
workInProgress.type != null &&
workInProgress.type.unstable_asyncUpdates === true
workInProgress.type.prototype != null &&
workInProgress.type.prototype.unstable_isAsyncReactComponent === true
) {
workInProgress.internalContextTag |= AsyncUpdates;
}
Expand Down
3 changes: 2 additions & 1 deletion src/renderers/shared/fiber/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
ReactFeatureFlags.enableAsyncSubtreeAPI &&
element != null &&
element.type != null &&
(element.type: any).unstable_asyncUpdates === true;
element.type.prototype != null &&
(element.type.prototype: any).unstable_isAsyncReactComponent === true;
const priorityLevel = getPriorityContext(current, forceAsync);
const nextState = {element};
callback = callback === undefined ? null : callback;
Expand Down

0 comments on commit ae430b7

Please sign in to comment.