proxy-based Twister client written with react-js
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
9.0 KiB

/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactUpdates
*/
'use strict';
var CallbackQueue = require("./CallbackQueue");
var PooledClass = require("./PooledClass");
var ReactCurrentOwner = require("./ReactCurrentOwner");
var ReactPerf = require("./ReactPerf");
var ReactReconciler = require("./ReactReconciler");
var Transaction = require("./Transaction");
var assign = require("./Object.assign");
var invariant = require("./invariant");
var warning = require("./warning");
var dirtyComponents = [];
var asapCallbackQueue = CallbackQueue.getPooled();
var asapEnqueued = false;
var batchingStrategy = null;
function ensureInjected() {
("production" !== process.env.NODE_ENV ? invariant(
ReactUpdates.ReactReconcileTransaction && batchingStrategy,
'ReactUpdates: must inject a reconcile transaction class and batching ' +
'strategy'
) : invariant(ReactUpdates.ReactReconcileTransaction && batchingStrategy));
}
var NESTED_UPDATES = {
initialize: function() {
this.dirtyComponentsLength = dirtyComponents.length;
},
close: function() {
if (this.dirtyComponentsLength !== dirtyComponents.length) {
// Additional updates were enqueued by componentDidUpdate handlers or
// similar; before our own UPDATE_QUEUEING wrapper closes, we want to run
// these new updates so that if A's componentDidUpdate calls setState on
// B, B will update before the callback A's updater provided when calling
// setState.
dirtyComponents.splice(0, this.dirtyComponentsLength);
flushBatchedUpdates();
} else {
dirtyComponents.length = 0;
}
}
};
var UPDATE_QUEUEING = {
initialize: function() {
this.callbackQueue.reset();
},
close: function() {
this.callbackQueue.notifyAll();
}
};
var TRANSACTION_WRAPPERS = [NESTED_UPDATES, UPDATE_QUEUEING];
function ReactUpdatesFlushTransaction() {
this.reinitializeTransaction();
this.dirtyComponentsLength = null;
this.callbackQueue = CallbackQueue.getPooled();
this.reconcileTransaction =
ReactUpdates.ReactReconcileTransaction.getPooled();
}
assign(
ReactUpdatesFlushTransaction.prototype,
Transaction.Mixin, {
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
destructor: function() {
this.dirtyComponentsLength = null;
CallbackQueue.release(this.callbackQueue);
this.callbackQueue = null;
ReactUpdates.ReactReconcileTransaction.release(this.reconcileTransaction);
this.reconcileTransaction = null;
},
perform: function(method, scope, a) {
// Essentially calls `this.reconcileTransaction.perform(method, scope, a)`
// with this transaction's wrappers around it.
return Transaction.Mixin.perform.call(
this,
this.reconcileTransaction.perform,
this.reconcileTransaction,
method,
scope,
a
);
}
});
PooledClass.addPoolingTo(ReactUpdatesFlushTransaction);
function batchedUpdates(callback, a, b, c, d) {
ensureInjected();
batchingStrategy.batchedUpdates(callback, a, b, c, d);
}
/**
* Array comparator for ReactComponents by mount ordering.
*
* @param {ReactComponent} c1 first component you're comparing
* @param {ReactComponent} c2 second component you're comparing
* @return {number} Return value usable by Array.prototype.sort().
*/
function mountOrderComparator(c1, c2) {
return c1._mountOrder - c2._mountOrder;
}
function runBatchedUpdates(transaction) {
var len = transaction.dirtyComponentsLength;
("production" !== process.env.NODE_ENV ? invariant(
len === dirtyComponents.length,
'Expected flush transaction\'s stored dirty-components length (%s) to ' +
'match dirty-components array length (%s).',
len,
dirtyComponents.length
) : invariant(len === dirtyComponents.length));
// Since reconciling a component higher in the owner hierarchy usually (not
// always -- see shouldComponentUpdate()) will reconcile children, reconcile
// them before their children by sorting the array.
dirtyComponents.sort(mountOrderComparator);
for (var i = 0; i < len; i++) {
// If a component is unmounted before pending changes apply, it will still
// be here, but we assume that it has cleared its _pendingCallbacks and
// that performUpdateIfNecessary is a noop.
var component = dirtyComponents[i];
// 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;
ReactReconciler.performUpdateIfNecessary(
component,
transaction.reconcileTransaction
);
if (callbacks) {
for (var j = 0; j < callbacks.length; j++) {
transaction.callbackQueue.enqueue(
callbacks[j],
component.getPublicInstance()
);
}
}
}
}
var flushBatchedUpdates = function() {
// ReactUpdatesFlushTransaction's wrappers will clear the dirtyComponents
// array and perform any updates enqueued by mount-ready handlers (i.e.,
// componentDidUpdate) but we need to check here too in order to catch
// updates enqueued by setState callbacks and asap calls.
while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
transaction.perform(runBatchedUpdates, null, transaction);
ReactUpdatesFlushTransaction.release(transaction);
}
if (asapEnqueued) {
asapEnqueued = false;
var queue = asapCallbackQueue;
asapCallbackQueue = CallbackQueue.getPooled();
queue.notifyAll();
CallbackQueue.release(queue);
}
}
};
flushBatchedUpdates = ReactPerf.measure(
'ReactUpdates',
'flushBatchedUpdates',
flushBatchedUpdates
);
/**
* Mark a component as needing a rerender, adding an optional callback to a
* list of functions which will be executed once the rerender occurs.
*/
function enqueueUpdate(component) {
ensureInjected();
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (This is called by each top-level update
// function, like setProps, setState, forceUpdate, etc.; creation and
// destruction of top-level components is guarded in ReactMount.)
("production" !== process.env.NODE_ENV ? warning(
ReactCurrentOwner.current == null,
'enqueueUpdate(): Render methods should be a pure function of props ' +
'and state; triggering nested component updates from render is not ' +
'allowed. If necessary, trigger nested updates in ' +
'componentDidUpdate.'
) : null);
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
}
/**
* Enqueue a callback to be run at the end of the current batching cycle. Throws
* if no updates are currently being performed.
*/
function asap(callback, context) {
("production" !== process.env.NODE_ENV ? invariant(
batchingStrategy.isBatchingUpdates,
'ReactUpdates.asap: Can\'t enqueue an asap callback in a context where' +
'updates are not being batched.'
) : invariant(batchingStrategy.isBatchingUpdates));
asapCallbackQueue.enqueue(callback, context);
asapEnqueued = true;
}
var ReactUpdatesInjection = {
injectReconcileTransaction: function(ReconcileTransaction) {
("production" !== process.env.NODE_ENV ? invariant(
ReconcileTransaction,
'ReactUpdates: must provide a reconcile transaction class'
) : invariant(ReconcileTransaction));
ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
},
injectBatchingStrategy: function(_batchingStrategy) {
("production" !== process.env.NODE_ENV ? invariant(
_batchingStrategy,
'ReactUpdates: must provide a batching strategy'
) : invariant(_batchingStrategy));
("production" !== process.env.NODE_ENV ? invariant(
typeof _batchingStrategy.batchedUpdates === 'function',
'ReactUpdates: must provide a batchedUpdates() function'
) : invariant(typeof _batchingStrategy.batchedUpdates === 'function'));
("production" !== process.env.NODE_ENV ? invariant(
typeof _batchingStrategy.isBatchingUpdates === 'boolean',
'ReactUpdates: must provide an isBatchingUpdates boolean attribute'
) : invariant(typeof _batchingStrategy.isBatchingUpdates === 'boolean'));
batchingStrategy = _batchingStrategy;
}
};
var ReactUpdates = {
/**
* React references `ReactReconcileTransaction` using this property in order
* to allow dependency injection.
*
* @internal
*/
ReactReconcileTransaction: null,
batchedUpdates: batchedUpdates,
enqueueUpdate: enqueueUpdate,
flushBatchedUpdates: flushBatchedUpdates,
injection: ReactUpdatesInjection,
asap: asap
};
module.exports = ReactUpdates;