react-native
Version:
A framework for building native apps using React
250 lines (220 loc) • 8.27 kB
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.
*
* @providesModule ReactUpdateQueue
*/
;
var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactUpdates = require('ReactUpdates');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
var warnOnInvalidCallback = function(callback : mixed, callerName : string) {
warning(
callback === null || typeof callback === 'function',
'%s(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callerName,
String(callback)
);
};
}
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
var internalInstance = ReactInstanceMap.get(publicInstance);
if (!internalInstance) {
if (__DEV__) {
var ctor = publicInstance.constructor;
warning(
false,
'Can only update a mounted or mounting component. This usually means ' +
'you called setState, replaceState, or forceUpdate on an unmounted ' +
'component. This is a no-op.\n\nPlease check the code for the ' +
'%s component.',
ctor && (ctor.displayName || ctor.name) || 'ReactClass'
);
}
return null;
}
if (__DEV__) {
warning(
ReactCurrentOwner.current == null,
'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`.',
);
}
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 (__DEV__) {
var owner = ReactCurrentOwner.current;
if (owner !== null) {
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'
);
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;
}
},
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.
* @param {?function} callback Called after component is updated.
* @param {?string} Name of the calling function in the public API.
* @internal
*/
enqueueForceUpdate: function(publicInstance, callback, callerName) {
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
}
if (callback) {
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, callerName);
}
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
}
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.
* @param {?function} callback Called after state is updated.
* @param {?string} Name of the calling function in the public API.
* @internal
*/
enqueueReplaceState: function(publicInstance, completeState, callback, callerName) {
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
}
internalInstance._pendingStateQueue = [completeState];
internalInstance._pendingReplaceState = true;
if (callback) {
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, callerName);
}
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.
* @param {?function} callback Called after state is updated.
* @param {?string} Name of the calling function in the public API.
* @internal
*/
enqueueSetState: function(publicInstance, partialState, callback, callerName) {
if (__DEV__) {
ReactInstrumentation.debugTool.onSetState();
warning(
partialState != null,
'setState(...): You passed an undefined or null state object; ' +
'instead, use forceUpdate().'
);
}
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
if (!internalInstance) {
return;
}
var queue =
internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
queue.push(partialState);
if (callback) {
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, callerName);
}
if (internalInstance._pendingCallbacks) {
internalInstance._pendingCallbacks.push(callback);
} else {
internalInstance._pendingCallbacks = [callback];
}
}
enqueueUpdate(internalInstance);
},
enqueueElementInternal: function(internalInstance, nextElement, nextContext) {
internalInstance._pendingElement = nextElement;
// TODO: introduce _pendingContext instead of setting it directly.
internalInstance._context = nextContext;
enqueueUpdate(internalInstance);
},
};
module.exports = ReactUpdateQueue;