Skip to content

Commit adae02b

Browse files
sophiebitszpao
authored andcommitted
Add ReactUpdates.setImmediate for async callbacks
Callbacks passed to this setImmediate function are called at the end of the current update cycle, which is guaranteed to be asynchronous but in the same event loop (with the default batching strategy). This is useful for new-style refs (facebook#1373, facebook#1554) and for fixing facebook#1698. Test Plan: jest
1 parent c419cce commit adae02b

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

src/core/ReactUpdates.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ var mixInto = require('mixInto');
2929
var warning = require('warning');
3030

3131
var dirtyComponents = [];
32+
var setImmediateCallbackQueue = CallbackQueue.getPooled();
33+
var setImmediateEnqueued = false;
3234

3335
var batchingStrategy = null;
3436

@@ -73,7 +75,7 @@ var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
7375
function ReactUpdatesFlushTransaction() {
7476
this.reinitializeTransaction();
7577
this.dirtyComponentsLength = null;
76-
this.callbackQueue = CallbackQueue.getPooled(null);
78+
this.callbackQueue = CallbackQueue.getPooled();
7779
this.reconcileTransaction =
7880
ReactUpdates.ReactReconcileTransaction.getPooled();
7981
}
@@ -170,8 +172,8 @@ var flushBatchedUpdates = ReactPerf.measure(
170172
// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
171173
// array and perform any updates enqueued by mount-ready handlers (i.e.,
172174
// componentDidUpdate) but we need to check here too in order to catch
173-
// updates enqueued by setState callbacks.
174-
while (dirtyComponents.length) {
175+
// updates enqueued by setState callbacks and setImmediate calls.
176+
while (dirtyComponents.length || setImmediateEnqueued) {
175177
var allUnmounted = true;
176178
for (var i = 0, l = dirtyComponents.length; i < l; i++) {
177179
if (dirtyComponents[i].isMounted()) {
@@ -194,6 +196,10 @@ var flushBatchedUpdates = ReactPerf.measure(
194196
var transaction = ReactUpdatesFlushTransaction.getPooled();
195197
transaction.perform(runBatchedUpdates, null, transaction);
196198
ReactUpdatesFlushTransaction.release(transaction);
199+
200+
setImmediateCallbackQueue.notifyAll();
201+
setImmediateCallbackQueue.reset();
202+
setImmediateEnqueued = false;
197203
}
198204
}
199205
);
@@ -240,6 +246,20 @@ function enqueueUpdate(component, callback) {
240246
}
241247
}
242248

249+
/**
250+
* Enqueue a callback to be run at the end of the current batching cycle. Throws
251+
* if no updates are currently being performed.
252+
*/
253+
function setImmediate(callback, context) {
254+
invariant(
255+
batchingStrategy.isBatchingUpdates,
256+
'ReactUpdates.setImmediate: Can\'t enqueue an immediate callback in a ' +
257+
'context where updates are not being batched.'
258+
);
259+
setImmediateCallbackQueue.enqueue(callback, context);
260+
setImmediateEnqueued = true;
261+
}
262+
243263
var ReactUpdatesInjection = {
244264
injectReconcileTransaction: function(ReconcileTransaction) {
245265
invariant(
@@ -278,7 +298,8 @@ var ReactUpdates = {
278298
batchedUpdates: batchedUpdates,
279299
enqueueUpdate: enqueueUpdate,
280300
flushBatchedUpdates: flushBatchedUpdates,
281-
injection: ReactUpdatesInjection
301+
injection: ReactUpdatesInjection,
302+
setImmediate: setImmediate
282303
};
283304

284305
module.exports = ReactUpdates;

src/core/__tests__/ReactUpdates-test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,4 +748,26 @@ describe('ReactUpdates', function() {
748748
React.renderComponent(<A x={2} />, container);
749749
expect(callbackCount).toBe(1);
750750
});
751+
752+
it('calls setImmediate callbacks properly', function() {
753+
var callbackCount = 0;
754+
var A = React.createClass({
755+
render: function() {
756+
return <div />;
757+
},
758+
componentDidUpdate: function() {
759+
var component = this;
760+
ReactUpdates.setImmediate(function() {
761+
expect(this).toBe(component);
762+
callbackCount++;
763+
}, this);
764+
expect(callbackCount).toBe(0);
765+
}
766+
});
767+
768+
var container = document.createElement('div');
769+
var component = React.renderComponent(<A />, container);
770+
component.forceUpdate();
771+
expect(callbackCount).toBe(1);
772+
});
751773
});

0 commit comments

Comments
 (0)