233 lines
9.1 KiB
JavaScript
233 lines
9.1 KiB
JavaScript
/**
|
|
* Copyright 2015-present, 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.
|
|
*
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var _prodInvariant = require('./reactProdInvariant');
|
|
|
|
var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
|
|
var ReactInstanceMap = require('./ReactInstanceMap');
|
|
var ReactInstrumentation = require('./ReactInstrumentation');
|
|
var ReactUpdates = require('./ReactUpdates');
|
|
|
|
var invariant = require('fbjs/lib/invariant');
|
|
var warning = require('fbjs/lib/warning');
|
|
|
|
function enqueueUpdate(internalInstance) {
|
|
ReactUpdates.enqueueUpdate(internalInstance);
|
|
}
|
|
|
|
function formatUnexpectedArgument(arg) {
|
|
var type = typeof arg;
|
|
if (type !== 'object') {
|
|
return type;
|
|
}
|
|
var displayName = arg.constructor && arg.constructor.name || type;
|
|
var keys = Object.keys(arg);
|
|
if (keys.length > 0 && keys.length < 20) {
|
|
return displayName + ' (keys: ' + keys.join(', ') + ')';
|
|
}
|
|
return displayName;
|
|
}
|
|
|
|
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
|
|
var internalInstance = ReactInstanceMap.get(publicInstance);
|
|
if (!internalInstance) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var ctor = publicInstance.constructor;
|
|
// Only warn when we have a callerName. Otherwise we should be silent.
|
|
// We're probably calling from enqueueCallback. We don't want to warn
|
|
// there because we already warned for the corresponding lifecycle method.
|
|
process.env.NODE_ENV !== 'production' ? warning(!callerName, '%s(...): Can only update a mounted or mounting component. ' + 'This usually means you called %s() on an unmounted component. ' + 'This is a no-op. Please check the code for the %s component.', callerName, callerName, ctor && (ctor.displayName || ctor.name) || 'ReactClass') : void 0;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
process.env.NODE_ENV !== 'production' ? warning(ReactCurrentOwner.current == null, '%s(...): Cannot update during an existing state transition (such as ' + "within `render` or another component's constructor). Render methods " + 'should be a pure function of props and state; constructor ' + 'side-effects are an anti-pattern, but can be moved to ' + '`componentWillMount`.', callerName) : void 0;
|
|
}
|
|
|
|
return internalInstance;
|
|
}
|
|
|
|
/**
|
|
* ReactUpdateQueue allows for state updates to be scheduled into a later
|
|
* reconciliation step.
|
|
*/
|
|
var ReactUpdateQueue = {
|
|
/**
|
|
* Checks whether or not this composite component is mounted.
|
|
* @param {ReactClass} publicInstance The instance we want to test.
|
|
* @return {boolean} True if mounted, false otherwise.
|
|
* @protected
|
|
* @final
|
|
*/
|
|
isMounted: function (publicInstance) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
var owner = ReactCurrentOwner.current;
|
|
if (owner !== null) {
|
|
process.env.NODE_ENV !== 'production' ? warning(owner._warnedAboutRefsInRender, '%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', owner.getName() || 'A component') : void 0;
|
|
owner._warnedAboutRefsInRender = true;
|
|
}
|
|
}
|
|
var internalInstance = ReactInstanceMap.get(publicInstance);
|
|
if (internalInstance) {
|
|
// During componentWillMount and render this will still be null but after
|
|
// that will always render to something. At least for now. So we can use
|
|
// this hack.
|
|
return !!internalInstance._renderedComponent;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Enqueue a callback that will be executed after all the pending updates
|
|
* have processed.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance to use as `this` context.
|
|
* @param {?function} callback Called after state is updated.
|
|
* @param {string} callerName Name of the calling function in the public API.
|
|
* @internal
|
|
*/
|
|
enqueueCallback: function (publicInstance, callback, callerName) {
|
|
ReactUpdateQueue.validateCallback(callback, callerName);
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
|
|
|
|
// Previously we would throw an error if we didn't have an internal
|
|
// instance. Since we want to make it a no-op instead, we mirror the same
|
|
// behavior we have in other enqueue* methods.
|
|
// We also need to ignore callbacks in componentWillMount. See
|
|
// enqueueUpdates.
|
|
if (!internalInstance) {
|
|
return null;
|
|
}
|
|
|
|
if (internalInstance._pendingCallbacks) {
|
|
internalInstance._pendingCallbacks.push(callback);
|
|
} else {
|
|
internalInstance._pendingCallbacks = [callback];
|
|
}
|
|
// TODO: The callback here is ignored when setState is called from
|
|
// componentWillMount. Either fix it or disallow doing so completely in
|
|
// favor of getInitialState. Alternatively, we can disallow
|
|
// componentWillMount during server-side rendering.
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
enqueueCallbackInternal: function (internalInstance, callback) {
|
|
if (internalInstance._pendingCallbacks) {
|
|
internalInstance._pendingCallbacks.push(callback);
|
|
} else {
|
|
internalInstance._pendingCallbacks = [callback];
|
|
}
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
/**
|
|
* Forces an update. This should only be invoked when it is known with
|
|
* certainty that we are **not** in a DOM transaction.
|
|
*
|
|
* You may want to call this when you know that some deeper aspect of the
|
|
* component's state has changed but `setState` was not called.
|
|
*
|
|
* This will not invoke `shouldComponentUpdate`, but it will invoke
|
|
* `componentWillUpdate` and `componentDidUpdate`.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @internal
|
|
*/
|
|
enqueueForceUpdate: function (publicInstance) {
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'forceUpdate');
|
|
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
|
|
internalInstance._pendingForceUpdate = true;
|
|
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
/**
|
|
* Replaces all of the state. Always use this or `setState` to mutate state.
|
|
* You should treat `this.state` as immutable.
|
|
*
|
|
* There is no guarantee that `this.state` will be immediately updated, so
|
|
* accessing `this.state` after calling this method may return the old value.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} completeState Next state.
|
|
* @internal
|
|
*/
|
|
enqueueReplaceState: function (publicInstance, completeState, callback) {
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'replaceState');
|
|
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
|
|
internalInstance._pendingStateQueue = [completeState];
|
|
internalInstance._pendingReplaceState = true;
|
|
|
|
// Future-proof 15.5
|
|
if (callback !== undefined && callback !== null) {
|
|
ReactUpdateQueue.validateCallback(callback, 'replaceState');
|
|
if (internalInstance._pendingCallbacks) {
|
|
internalInstance._pendingCallbacks.push(callback);
|
|
} else {
|
|
internalInstance._pendingCallbacks = [callback];
|
|
}
|
|
}
|
|
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
/**
|
|
* Sets a subset of the state. This only exists because _pendingState is
|
|
* internal. This provides a merging strategy that is not available to deep
|
|
* properties which is confusing. TODO: Expose pendingState or don't use it
|
|
* during the merge.
|
|
*
|
|
* @param {ReactClass} publicInstance The instance that should rerender.
|
|
* @param {object} partialState Next partial state to be merged with state.
|
|
* @internal
|
|
*/
|
|
enqueueSetState: function (publicInstance, partialState) {
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
ReactInstrumentation.debugTool.onSetState();
|
|
process.env.NODE_ENV !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : void 0;
|
|
}
|
|
|
|
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
|
|
|
|
if (!internalInstance) {
|
|
return;
|
|
}
|
|
|
|
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
|
|
queue.push(partialState);
|
|
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
enqueueElementInternal: function (internalInstance, nextElement, nextContext) {
|
|
internalInstance._pendingElement = nextElement;
|
|
// TODO: introduce _pendingContext instead of setting it directly.
|
|
internalInstance._context = nextContext;
|
|
enqueueUpdate(internalInstance);
|
|
},
|
|
|
|
validateCallback: function (callback, callerName) {
|
|
!(!callback || typeof callback === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s(...): Expected the last optional `callback` argument to be a function. Instead received: %s.', callerName, formatUnexpectedArgument(callback)) : _prodInvariant('122', callerName, formatUnexpectedArgument(callback)) : void 0;
|
|
}
|
|
};
|
|
|
|
module.exports = ReactUpdateQueue; |