Make React batching strategy injectable

This commit is contained in:
Pete Hunt
2013-08-30 13:20:48 -07:00
committed by Paul O’Shannessy
parent f88aa35187
commit 0db4077c3a
3 changed files with 144 additions and 39 deletions
+74
View File
@@ -0,0 +1,74 @@
/**
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @providesModule ReactDefaultBatchingStrategy
*/
"use strict";
var ReactUpdates = require('ReactUpdates');
var Transaction = require('Transaction');
var emptyFunction = require('emptyFunction');
var mixInto = require('mixInto');
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function() {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
function ReactDefaultBatchingStrategyTransaction() {
this.reinitializeTransaction();
}
mixInto(ReactDefaultBatchingStrategyTransaction, Transaction.Mixin);
mixInto(ReactDefaultBatchingStrategyTransaction, {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
}
});
var ReactDefaultBatchingStrategy = {
isBatchingUpdates: false,
/**
* 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) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// The code is written this way to avoid extra allocations
if (alreadyBatchingUpdates) {
callback();
} else {
var transaction = new ReactDefaultBatchingStrategyTransaction();
transaction.perform(callback);
}
}
};
module.exports = ReactDefaultBatchingStrategy;
+7
View File
@@ -40,6 +40,9 @@ var ReactInstanceHandles = require('ReactInstanceHandles');
var SimpleEventPlugin = require('SimpleEventPlugin');
var MobileSafariClickEventPlugin = require('MobileSafariClickEventPlugin');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactUpdates = require('ReactUpdates');
function inject() {
ReactEventEmitter.TopLevelCallbackCreator = ReactEventTopLevelCallback;
/**
@@ -73,6 +76,10 @@ function inject() {
if (__DEV__) {
ReactPerf.injection.injectMeasure(require('ReactDefaultPerf').measure);
}
ReactUpdates.injection.injectBatchingStrategy(
ReactDefaultBatchingStrategy
);
}
module.exports = {
+63 -39
View File
@@ -20,51 +20,54 @@
var invariant = require('invariant');
var isBatchingUpdates = false;
var dirtyComponents = [];
/**
* Call the provided function in a context within which calls to `setState` and
* friends are batched such that components aren't updated unnecessarily.
*/
var batchingStrategy = null;
function ensureBatchingStrategy() {
invariant(batchingStrategy, 'ReactUpdates: must inject a batching strategy');
}
function batchedUpdates(callback) {
if (isBatchingUpdates) {
// We're already executing in an environment where updates will be batched,
// so this is a no-op.
callback();
return;
}
ensureBatchingStrategy();
batchingStrategy.batchedUpdates(callback);
}
isBatchingUpdates = true;
try {
callback();
// TODO: Sort components by depth such that parent components update first
for (var i = 0; i < dirtyComponents.length; i++) {
// If a component is unmounted before pending changes apply, ignore them
// TODO: Queue unmounts in the same list to avoid this happening at all
var component = dirtyComponents[i];
if (component.isMounted()) {
// If performUpdateIfNecessary happens to enqueue any new updates, we
// shouldn't execute the callbacks until the next render happens, so
// stash the callbacks first
var callbacks = component._pendingCallbacks;
component._pendingCallbacks = null;
component.performUpdateIfNecessary();
if (callbacks) {
for (var j = 0; j < callbacks.length; j++) {
callbacks[j].call(component);
}
function runBatchedUpdates() {
// TODO: Sort components by depth such that parent components update first
for (var i = 0; i < dirtyComponents.length; i++) {
// If a component is unmounted before pending changes apply, ignore them
// TODO: Queue unmounts in the same list to avoid this happening at all
var component = dirtyComponents[i];
if (component.isMounted()) {
// If performUpdateIfNecessary happens to enqueue any new updates, we
// shouldn't execute the callbacks until the next render happens, so
// stash the callbacks first
var callbacks = component._pendingCallbacks;
component._pendingCallbacks = null;
component.performUpdateIfNecessary();
if (callbacks) {
for (var j = 0; j < callbacks.length; j++) {
callbacks[j].call(component);
}
}
}
} catch (error) {
// IE8 requires `catch` in order to use `finally`.
throw error;
}
}
function clearDirtyComponents() {
dirtyComponents.length = 0;
}
function flushBatchedUpdates() {
// Run these in separate functions so the JIT can optimize
try {
runBatchedUpdates();
} catch (e) {
// IE 8 requires catch to use finally.
throw e;
} finally {
dirtyComponents.length = 0;
isBatchingUpdates = false;
clearDirtyComponents();
}
}
@@ -79,8 +82,9 @@ function enqueueUpdate(component, callback) {
'`setState`, `replaceState`, or `forceUpdate` with a callback that ' +
'isn\'t callable.'
);
ensureBatchingStrategy();
if (!isBatchingUpdates) {
if (!batchingStrategy.isBatchingUpdates) {
component.performUpdateIfNecessary();
callback && callback();
return;
@@ -97,9 +101,29 @@ function enqueueUpdate(component, callback) {
}
}
var ReactUpdatesInjection = {
injectBatchingStrategy: function(_batchingStrategy) {
invariant(
_batchingStrategy,
'ReactUpdates: must provide a batching strategy'
);
invariant(
typeof _batchingStrategy.batchedUpdates === 'function',
'ReactUpdates: must provide a batchedUpdates() function'
);
invariant(
typeof _batchingStrategy.isBatchingUpdates === 'boolean',
'ReactUpdates: must provide an isBatchingUpdates boolean attribute'
);
batchingStrategy = _batchingStrategy;
}
};
var ReactUpdates = {
batchedUpdates: batchedUpdates,
enqueueUpdate: enqueueUpdate
enqueueUpdate: enqueueUpdate,
flushBatchedUpdates: flushBatchedUpdates,
injection: ReactUpdatesInjection
};
module.exports = ReactUpdates;