Batch updates in initial render

Currently, the first setState that happens during initial render will
start a new batch. Any subsequent updates will be batched. That means that
the first setState is synchronous but any subsequent setStates are
asynchronous.

This commit makes it so that the batching starts at the root. That way all
the setStates that happen within life-cycle methods are asynchronous.
This commit is contained in:
Sebastian Markbage
2015-01-27 01:00:59 -08:00
parent b3df7cabb7
commit 9811b10aaa
5 changed files with 46 additions and 19 deletions
+2 -2
View File
@@ -31,8 +31,8 @@ var ReactRAFBatchingStrategy = {
* Call the provided function in a context within which calls to `setState`
* and friends are batched such that components aren't updated unnecessarily.
*/
batchedUpdates: function(callback, a, b) {
callback(a, b);
batchedUpdates: function(callback, a, b, c, d) {
callback(a, b, c, d);
}
};
+36 -7
View File
@@ -233,23 +233,51 @@ function findDeepestCachedAncestor(targetID) {
/**
* Mounts this component and inserts it into the DOM.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {string} rootID DOM ID of the root node.
* @param {DOMElement} container DOM element to mount into.
* @param {ReactReconcileTransaction} transaction
* @param {boolean} shouldReuseMarkup If true, do not insert markup
*/
function mountComponentIntoNode(
componentInstance,
rootID,
container,
transaction,
shouldReuseMarkup) {
var markup = ReactReconciler.mountComponent(
this, rootID, transaction, emptyObject
componentInstance, rootID, transaction, emptyObject
);
this._isTopLevel = true;
componentInstance._isTopLevel = true;
ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup);
}
/**
* Batched mount.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {string} rootID DOM ID of the root node.
* @param {DOMElement} container DOM element to mount into.
* @param {boolean} shouldReuseMarkup If true, do not insert markup
*/
function batchedMountComponentIntoNode(
componentInstance,
rootID,
container,
shouldReuseMarkup) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
transaction.perform(
mountComponentIntoNode,
null,
componentInstance,
rootID,
container,
transaction,
shouldReuseMarkup
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
/**
* Mounting is the process of initializing a React component by creatings its
* representative DOM elements and inserting them into a supplied `container`.
@@ -368,16 +396,17 @@ var ReactMount = {
container
);
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
transaction.perform(
mountComponentIntoNode,
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
componentInstance,
reactRootID,
container,
transaction,
shouldReuseMarkup
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
if (__DEV__) {
// Record the root element in case it later gets transplanted.
+3 -3
View File
@@ -54,16 +54,16 @@ var ReactDefaultBatchingStrategy = {
* Call the provided function in a context within which calls to `setState`
* and friends are batched such that components aren't updated unnecessarily.
*/
batchedUpdates: function(callback, a, b) {
batchedUpdates: function(callback, a, b, c, d) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
callback(a, b);
callback(a, b, c, d);
} else {
transaction.perform(callback, null, a, b);
transaction.perform(callback, null, a, b, c, d);
}
}
};
+2 -2
View File
@@ -104,9 +104,9 @@ assign(
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
function batchedUpdates(callback, a, b) {
function batchedUpdates(callback, a, b, c, d) {
ensureInjected();
batchingStrategy.batchedUpdates(callback, a, b);
batchingStrategy.batchedUpdates(callback, a, b, c, d);
}
/**
@@ -155,9 +155,9 @@ describe('ReactCompositeComponent-state', function() {
[ 'componentDidMount-start', 'orange', null ],
// setState-sunrise and setState-orange should be called here,
// after the bug in #1740
// componentDidMount() called setState({color:'yellow'}), currently this
// occurs inline.
// In a future where setState() is async, this test result will change.
// componentDidMount() called setState({color:'yellow'}), which is async.
// The update doesn't happen until the next flush.
[ 'componentDidMount-end', 'orange', 'yellow' ],
[ 'shouldComponentUpdate-currentState', 'orange', null ],
[ 'shouldComponentUpdate-nextState', 'yellow' ],
[ 'componentWillUpdate-currentState', 'orange', null ],
@@ -166,8 +166,6 @@ describe('ReactCompositeComponent-state', function() {
[ 'componentDidUpdate-currentState', 'yellow', null ],
[ 'componentDidUpdate-prevState', 'orange' ],
[ 'setState-yellow', 'yellow', null ],
// componentDidMount() finally closes.
[ 'componentDidMount-end', 'yellow', null ],
[ 'initial-callback', 'yellow', null ],
[ 'componentWillReceiveProps-start', 'yellow', null ],
// setState({color:'green'}) only enqueues a pending state.