Skip to content

Commit f9ad17d

Browse files
committed
Merge pull request facebook#1363 from spicyj/batch-all
Batch subupdates caused by any state update
2 parents 49652c8 + e17086e commit f9ad17d

File tree

4 files changed

+282
-38
lines changed

4 files changed

+282
-38
lines changed

src/addons/ReactRAFBatchingStrategy.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ var ReactRAFBatchingStrategy = {
3636
* Call the provided function in a context within which calls to `setState`
3737
* and friends are batched such that components aren't updated unnecessarily.
3838
*/
39-
batchedUpdates: function(callback, param) {
40-
callback(param);
39+
batchedUpdates: function(callback, a, b) {
40+
callback(a, b);
4141
}
4242
};
4343

src/core/ReactDefaultBatchingStrategy.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ var ReactDefaultBatchingStrategy = {
5858
* Call the provided function in a context within which calls to `setState`
5959
* and friends are batched such that components aren't updated unnecessarily.
6060
*/
61-
batchedUpdates: function(callback, param) {
61+
batchedUpdates: function(callback, a, b) {
6262
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
6363

6464
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
6565

6666
// The code is written this way to avoid extra allocations
6767
if (alreadyBatchingUpdates) {
68-
callback(param);
68+
callback(a, b);
6969
} else {
70-
transaction.perform(callback, null, param);
70+
transaction.perform(callback, null, a, b);
7171
}
7272
}
7373
};

src/core/ReactUpdates.js

Lines changed: 94 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@
1818

1919
"use strict";
2020

21+
var CallbackQueue = require('CallbackQueue');
22+
var PooledClass = require('PooledClass');
2123
var ReactCurrentOwner = require('ReactCurrentOwner');
2224
var ReactPerf = require('ReactPerf');
25+
var Transaction = require('Transaction');
2326

2427
var invariant = require('invariant');
28+
var mixInto = require('mixInto');
2529
var warning = require('warning');
2630

2731
var dirtyComponents = [];
@@ -36,9 +40,77 @@ function ensureInjected() {
3640
);
3741
}
3842

39-
function batchedUpdates(callback, param) {
43+
var NESTED_UPDATES = {
44+
initialize: function() {
45+
this.dirtyComponentsLength = dirtyComponents.length;
46+
},
47+
close: function() {
48+
if (this.dirtyComponentsLength !== dirtyComponents.length) {
49+
// Additional updates were enqueued by componentDidUpdate handlers or
50+
// similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
51+
// these new updates so that if A's componentDidUpdate calls setState on
52+
// B, B will update before the callback A's updater provided when calling
53+
// setState.
54+
dirtyComponents.splice(0, this.dirtyComponentsLength);
55+
flushBatchedUpdates();
56+
} else {
57+
dirtyComponents.length = 0;
58+
}
59+
}
60+
};
61+
62+
var UPDATE_QUEUEING = {
63+
initialize: function() {
64+
this.callbackQueue.reset();
65+
},
66+
close: function() {
67+
this.callbackQueue.notifyAll();
68+
}
69+
};
70+
71+
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
72+
73+
function ReactUpdatesFlushTransaction() {
74+
this.reinitializeTransaction();
75+
this.dirtyComponentsLength = null;
76+
this.callbackQueue = CallbackQueue.getPooled(null);
77+
this.reconcileTransaction =
78+
ReactUpdates.ReactReconcileTransaction.getPooled();
79+
}
80+
81+
mixInto(ReactUpdatesFlushTransaction, Transaction.Mixin);
82+
mixInto(ReactUpdatesFlushTransaction, {
83+
getTransactionWrappers: function() {
84+
return TRANSACTION_WRAPPERS;
85+
},
86+
87+
destructor: function() {
88+
this.dirtyComponentsLength = null;
89+
CallbackQueue.release(this.callbackQueue);
90+
this.callbackQueue = null;
91+
ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);
92+
this.reconcileTransaction = null;
93+
},
94+
95+
perform: function(method, scope, a) {
96+
// Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
97+
// with this transaction's wrappers around it.
98+
return Transaction.Mixin.perform.call(
99+
this,
100+
this.reconcileTransaction.perform,
101+
this.reconcileTransaction,
102+
method,
103+
scope,
104+
a
105+
);
106+
}
107+
});
108+
109+
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
110+
111+
function batchedUpdates(callback, a, b) {
40112
ensureInjected();
41-
batchingStrategy.batchedUpdates(callback, param);
113+
batchingStrategy.batchedUpdates(callback, a, b);
42114
}
43115

44116
/**
@@ -53,13 +125,21 @@ function mountDepthComparator(c1, c2) {
53125
}
54126

55127
function runBatchedUpdates(transaction) {
128+
var len = transaction.dirtyComponentsLength;
129+
invariant(
130+
len === dirtyComponents.length,
131+
'Expected flush transaction\'s stored dirty-components length (%s) to ' +
132+
'match dirty-components array length (%s).',
133+
len,
134+
dirtyComponents.length
135+
);
136+
56137
// Since reconciling a component higher in the owner hierarchy usually (not
57138
// always -- see shouldComponentUpdate()) will reconcile children, reconcile
58139
// them before their children by sorting the array.
59-
60140
dirtyComponents.sort(mountDepthComparator);
61141

62-
for (var i = 0; i < dirtyComponents.length; i++) {
142+
for (var i = 0; i < len; i++) {
63143
// If a component is unmounted before pending changes apply, ignore them
64144
// TODO: Queue unmounts in the same list to avoid this happening at all
65145
var component = dirtyComponents[i];
@@ -69,11 +149,11 @@ function runBatchedUpdates(transaction) {
69149
// stash the callbacks first
70150
var callbacks = component._pendingCallbacks;
71151
component._pendingCallbacks = null;
72-
component.performUpdateIfNecessary(transaction);
152+
component.performUpdateIfNecessary(transaction.reconcileTransaction);
73153

74154
if (callbacks) {
75155
for (var j = 0; j < callbacks.length; j++) {
76-
transaction.getReactMountReady().enqueue(
156+
transaction.callbackQueue.enqueue(
77157
callbacks[j],
78158
component
79159
);
@@ -83,30 +163,18 @@ function runBatchedUpdates(transaction) {
83163
}
84164
}
85165

86-
function clearDirtyComponents() {
87-
dirtyComponents.length = 0;
88-
}
89-
90-
function flushBatchedUpdatesOnce(transaction) {
91-
// Run these in separate functions so the JIT can optimize
92-
try {
93-
runBatchedUpdates(transaction);
94-
} finally {
95-
clearDirtyComponents();
96-
}
97-
}
98-
99166
var flushBatchedUpdates = ReactPerf.measure(
100167
'ReactUpdates',
101168
'flushBatchedUpdates',
102169
function() {
103-
// flushBatchedUpdatesOnce will clear the dirtyComponents array, but
104-
// mount-ready handlers (i.e., componentDidMount/Update) may enqueue more
105-
// state updates which we should apply immediately
170+
// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
171+
// array and perform any updates enqueued by mount-ready handlers (i.e.,
172+
// componentDidUpdate) but we need to check here too in order to catch
173+
// updates enqueued by setState callbacks.
106174
while (dirtyComponents.length) {
107-
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
108-
transaction.perform(flushBatchedUpdatesOnce, null, transaction);
109-
ReactUpdates.ReactReconcileTransaction.release(transaction);
175+
var transaction = ReactUpdatesFlushTransaction.getPooled();
176+
transaction.perform(runBatchedUpdates, null, transaction);
177+
ReactUpdatesFlushTransaction.release(transaction);
110178
}
111179
}
112180
);
@@ -138,14 +206,7 @@ function enqueueUpdate(component, callback) {
138206
);
139207

140208
if (!batchingStrategy.isBatchingUpdates) {
141-
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
142-
transaction.perform(
143-
component.performUpdateIfNecessary,
144-
component,
145-
transaction
146-
);
147-
ReactUpdates.ReactReconcileTransaction.release(transaction);
148-
callback && callback.call(component);
209+
batchingStrategy.batchedUpdates(enqueueUpdate, component, callback);
149210
return;
150211
}
151212

0 commit comments

Comments
 (0)