UNPKG

react-native

Version:

A framework for building native apps using React

250 lines (220 loc) • 8.27 kB
/** * 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 */ 'use strict'; 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;