mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge pull request #2930 from sebmarkbage/updatequeue
Extract setState, setProps etc into ReactUpdateQueue
This commit is contained in:
@@ -22,6 +22,7 @@ var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactMarkupChecksum = require('ReactMarkupChecksum');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
@@ -299,9 +300,11 @@ var ReactMount = {
|
||||
ReactElementValidator.checkAndWarnForMutatedProps(nextElement);
|
||||
}
|
||||
|
||||
var nextProps = nextElement.props;
|
||||
ReactMount.scrollMonitor(container, function() {
|
||||
prevComponent.replaceProps(nextProps, callback);
|
||||
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
|
||||
}
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
|
||||
@@ -17,6 +17,7 @@ var ReactErrorUtils = require('ReactErrorUtils');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var invariant = require('invariant');
|
||||
@@ -719,16 +720,10 @@ var ReactClassMixin = {
|
||||
* type signature and the only use case for this, is to avoid that.
|
||||
*/
|
||||
replaceState: function(newState, callback) {
|
||||
var internalInstance = ReactInstanceMap.get(this);
|
||||
invariant(
|
||||
internalInstance,
|
||||
'replaceState(...): Can only update a mounted or mounting component. ' +
|
||||
'This usually means you called replaceState() on an unmounted component.'
|
||||
);
|
||||
internalInstance.replaceState(
|
||||
newState,
|
||||
callback
|
||||
);
|
||||
ReactUpdateQueue.enqueueReplaceState(this, newState);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -754,16 +749,10 @@ var ReactClassMixin = {
|
||||
* @deprecated
|
||||
*/
|
||||
setProps: function(partialProps, callback) {
|
||||
var internalInstance = ReactInstanceMap.get(this);
|
||||
invariant(
|
||||
internalInstance,
|
||||
'setProps(...): Can only update a mounted or mounting component. ' +
|
||||
'This usually means you called setProps() on an unmounted component.'
|
||||
);
|
||||
internalInstance.setProps(
|
||||
partialProps,
|
||||
callback
|
||||
);
|
||||
ReactUpdateQueue.enqueueSetProps(this, partialProps);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -776,10 +765,10 @@ var ReactClassMixin = {
|
||||
* @deprecated
|
||||
*/
|
||||
replaceProps: function(newProps, callback) {
|
||||
ReactInstanceMap.get(this).replaceProps(
|
||||
newProps,
|
||||
callback
|
||||
);
|
||||
ReactUpdateQueue.enqueueReplaceProps(this, newProps);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactElementValidator = require('ReactElementValidator');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactLifeCycle = require('ReactLifeCycle');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
@@ -27,8 +28,6 @@ var ReactUpdates = require('ReactUpdates');
|
||||
var assign = require('Object.assign');
|
||||
var emptyObject = require('emptyObject');
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
var monitorCodeUse = require('monitorCodeUse');
|
||||
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
|
||||
var warning = require('warning');
|
||||
|
||||
@@ -43,50 +42,6 @@ function getDeclarationErrorAddendum(component) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* `ReactCompositeComponent` maintains an auxiliary life cycle state in
|
||||
* `this._compositeLifeCycleState` (which can be null).
|
||||
*
|
||||
* This is different from the life cycle state maintained by `ReactComponent`.
|
||||
* The following diagram shows how the states overlap in
|
||||
* time. There are times when the CompositeLifeCycle is null - at those times it
|
||||
* is only meaningful to look at ComponentLifeCycle alone.
|
||||
*
|
||||
* Top Row: ReactComponent.ComponentLifeCycle
|
||||
* Low Row: ReactComponent.CompositeLifeCycle
|
||||
*
|
||||
* +-------+---------------------------------+--------+
|
||||
* | UN | MOUNTED | UN |
|
||||
* |MOUNTED| | MOUNTED|
|
||||
* +-------+---------------------------------+--------+
|
||||
* | ^--------+ +-------+ +--------^ |
|
||||
* | | | | | | | |
|
||||
* | 0--|MOUNTING|-0-|RECEIVE|-0-| UN |--->0 |
|
||||
* | | | |PROPS | |MOUNTING| |
|
||||
* | | | | | | | |
|
||||
* | | | | | | | |
|
||||
* | +--------+ +-------+ +--------+ |
|
||||
* | | | |
|
||||
* +-------+---------------------------------+--------+
|
||||
*/
|
||||
var CompositeLifeCycle = keyMirror({
|
||||
/**
|
||||
* Components in the process of being mounted respond to state changes
|
||||
* differently.
|
||||
*/
|
||||
MOUNTING: null,
|
||||
/**
|
||||
* Components in the process of being unmounted are guarded against state
|
||||
* changes.
|
||||
*/
|
||||
UNMOUNTING: null,
|
||||
/**
|
||||
* Components that are mounted and receiving new props respond to state
|
||||
* changes differently.
|
||||
*/
|
||||
RECEIVING_PROPS: null
|
||||
});
|
||||
|
||||
/**
|
||||
* An incrementing ID assigned to each component when it is mounted. This is
|
||||
* used to enforce the order in which `ReactUpdates` updates dirty components.
|
||||
@@ -112,6 +67,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._rootNodeID = null;
|
||||
this._instance = null;
|
||||
|
||||
// See ReactUpdateQueue
|
||||
this._pendingElement = null;
|
||||
this._pendingState = null;
|
||||
this._pendingForceUpdate = false;
|
||||
@@ -123,7 +79,7 @@ var ReactCompositeComponentMixin = {
|
||||
this._mountOrder = 0;
|
||||
this._isTopLevel = false;
|
||||
|
||||
// See ReactUpdates.
|
||||
// See ReactUpdates and ReactUpdateQueue.
|
||||
this._pendingCallbacks = null;
|
||||
},
|
||||
|
||||
@@ -134,7 +90,7 @@ var ReactCompositeComponentMixin = {
|
||||
* @final
|
||||
*/
|
||||
isMounted: function() {
|
||||
return this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING;
|
||||
return this._compositeLifeCycleState !== ReactLifeCycle.MOUNTING;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -171,7 +127,7 @@ var ReactCompositeComponentMixin = {
|
||||
// Store a reference from the instance back to the internal representation
|
||||
ReactInstanceMap.set(inst, this);
|
||||
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
|
||||
this._compositeLifeCycleState = ReactLifeCycle.MOUNTING;
|
||||
|
||||
if (__DEV__) {
|
||||
this._warnIfContextsDiffer(this._currentElement._context, context);
|
||||
@@ -265,7 +221,7 @@ var ReactCompositeComponentMixin = {
|
||||
unmountComponent: function() {
|
||||
var inst = this._instance;
|
||||
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;
|
||||
this._compositeLifeCycleState = ReactLifeCycle.UNMOUNTING;
|
||||
if (inst.componentWillUnmount) {
|
||||
inst.componentWillUnmount();
|
||||
}
|
||||
@@ -297,50 +253,6 @@ var ReactCompositeComponentMixin = {
|
||||
// TODO: inst.context = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a subset of the props.
|
||||
*
|
||||
* @param {object} partialProps Subset of the next props.
|
||||
* @param {?function} callback Called after props are updated.
|
||||
* @final
|
||||
* @public
|
||||
*/
|
||||
setProps: function(partialProps, callback) {
|
||||
// Merge with the pending element if it exists, otherwise with existing
|
||||
// element props.
|
||||
var element = this._pendingElement || this._currentElement;
|
||||
this.replaceProps(
|
||||
assign({}, element.props, partialProps),
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces all of the props.
|
||||
*
|
||||
* @param {object} props New props.
|
||||
* @param {?function} callback Called after props are updated.
|
||||
* @final
|
||||
* @public
|
||||
*/
|
||||
replaceProps: function(props, callback) {
|
||||
invariant(
|
||||
this._isTopLevel,
|
||||
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
|
||||
'component with a parent. This is an anti-pattern since props will ' +
|
||||
'get reactively updated when rendered. Instead, change the owner\'s ' +
|
||||
'`render` method to pass the correct value as props to the component ' +
|
||||
'where it is created.'
|
||||
);
|
||||
// This is a deoptimized path. We optimize for always having an element.
|
||||
// This creates an extra internal element.
|
||||
this._pendingElement = ReactElement.cloneAndReplaceProps(
|
||||
this._pendingElement || this._currentElement,
|
||||
props
|
||||
);
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Schedule a partial update to the props. Only used for internal testing.
|
||||
*
|
||||
@@ -360,109 +272,6 @@ var ReactCompositeComponentMixin = {
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 {object} partialState Next partial state to be merged with state.
|
||||
* @param {?function} callback Called after state is updated.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
setState: function(partialState, callback) {
|
||||
var compositeLifeCycleState = this._compositeLifeCycleState;
|
||||
invariant(
|
||||
ReactCurrentOwner.current == null,
|
||||
'setState(...): Cannot update during an existing state transition ' +
|
||||
'(such as within `render`). Render methods should be a pure function ' +
|
||||
'of props and state.'
|
||||
);
|
||||
invariant(
|
||||
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
|
||||
'setState(...): Cannot call setState() on an unmounting component.'
|
||||
);
|
||||
// Merge with `_pendingState` if it exists, otherwise with existing state.
|
||||
this._pendingState = assign(
|
||||
{},
|
||||
this._pendingState || this._instance.state,
|
||||
partialState
|
||||
);
|
||||
if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) {
|
||||
// If we're in a componentWillMount handler, don't enqueue a rerender
|
||||
// because ReactUpdates assumes we're in a browser context (which is wrong
|
||||
// for server rendering) and we're about to do a render anyway.
|
||||
// TODO: The callback here is ignored when setState is called from
|
||||
// componentWillMount. Either fix it or disallow doing so completely in
|
||||
// favor of getInitialState.
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 {object} completeState Next state.
|
||||
* @param {?function} callback Called after state is updated.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
replaceState: function(completeState, callback) {
|
||||
var compositeLifeCycleState = this._compositeLifeCycleState;
|
||||
invariant(
|
||||
ReactCurrentOwner.current == null,
|
||||
'replaceState(...): Cannot update during an existing state transition ' +
|
||||
'(such as within `render`). Render methods should be a pure function ' +
|
||||
'of props and state.'
|
||||
);
|
||||
invariant(
|
||||
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
|
||||
'replaceState(...): Cannot call replaceState() on an unmounting ' +
|
||||
'component.'
|
||||
);
|
||||
this._pendingState = completeState;
|
||||
if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) {
|
||||
// If we're in a componentWillMount handler, don't enqueue a rerender
|
||||
// because ReactUpdates assumes we're in a browser context (which is wrong
|
||||
// for server rendering) and we're about to do a render anyway.
|
||||
// TODO: The callback here is ignored when setState is called from
|
||||
// componentWillMount. Either fix it or disallow doing so completely in
|
||||
// favor of getInitialState.
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 `shouldUpdateComponent`, but it will invoke
|
||||
* `componentWillUpdate` and `componentDidUpdate`.
|
||||
*
|
||||
* @param {?function} callback Called after update is complete.isM
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
forceUpdate: function(callback) {
|
||||
var compositeLifeCycleState = this._compositeLifeCycleState;
|
||||
invariant(
|
||||
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
|
||||
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
|
||||
'forceUpdate(...): Cannot force an update while unmounting component ' +
|
||||
'or during an existing state transition (such as within `render`).'
|
||||
);
|
||||
this._pendingForceUpdate = true;
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Filters the context object to only contain keys specified in
|
||||
* `contextTypes`
|
||||
@@ -626,8 +435,8 @@ var ReactCompositeComponentMixin = {
|
||||
var compositeLifeCycleState = this._compositeLifeCycleState;
|
||||
// Do not trigger a state transition if we are in the middle of mounting or
|
||||
// receiving props because both of those will already be doing this.
|
||||
if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING ||
|
||||
compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) {
|
||||
if (compositeLifeCycleState === ReactLifeCycle.MOUNTING ||
|
||||
compositeLifeCycleState === ReactLifeCycle.RECEIVING_PROPS) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -743,7 +552,7 @@ var ReactCompositeComponentMixin = {
|
||||
}
|
||||
}
|
||||
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
|
||||
this._compositeLifeCycleState = ReactLifeCycle.RECEIVING_PROPS;
|
||||
if (inst.componentWillReceiveProps) {
|
||||
inst.componentWillReceiveProps(nextProps, nextContext);
|
||||
}
|
||||
@@ -1003,8 +812,6 @@ ReactPerf.measureMethods(
|
||||
|
||||
var ReactCompositeComponent = {
|
||||
|
||||
LifeCycle: CompositeLifeCycle,
|
||||
|
||||
Mixin: ReactCompositeComponentMixin
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright 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 ReactLifeCycle
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var keyMirror = require('keyMirror');
|
||||
|
||||
/**
|
||||
* `ReactCompositeComponent` maintains an auxiliary life cycle state in
|
||||
* `this._compositeLifeCycleState` (which can be null).
|
||||
*
|
||||
* This is different from the life cycle state maintained by `ReactComponent`.
|
||||
* The following diagram shows how the states overlap in
|
||||
* time. There are times when the CompositeLifeCycle is null - at those times it
|
||||
* is only meaningful to look at ComponentLifeCycle alone.
|
||||
*
|
||||
* Top Row: ReactComponent.ComponentLifeCycle
|
||||
* Low Row: ReactComponent.CompositeLifeCycle
|
||||
*
|
||||
* +-------+---------------------------------+--------+
|
||||
* | UN | MOUNTED | UN |
|
||||
* |MOUNTED| | MOUNTED|
|
||||
* +-------+---------------------------------+--------+
|
||||
* | ^--------+ +-------+ +--------^ |
|
||||
* | | | | | | | |
|
||||
* | 0--|MOUNTING|-0-|RECEIVE|-0-| UN |--->0 |
|
||||
* | | | |PROPS | |MOUNTING| |
|
||||
* | | | | | | | |
|
||||
* | | | | | | | |
|
||||
* | +--------+ +-------+ +--------+ |
|
||||
* | | | |
|
||||
* +-------+---------------------------------+--------+
|
||||
*/
|
||||
var ReactLifeCycle = keyMirror({
|
||||
/**
|
||||
* Components in the process of being mounted respond to state changes
|
||||
* differently.
|
||||
*/
|
||||
MOUNTING: null,
|
||||
/**
|
||||
* Components in the process of being unmounted are guarded against state
|
||||
* changes.
|
||||
*/
|
||||
UNMOUNTING: null,
|
||||
/**
|
||||
* Components that are mounted and receiving new props respond to state
|
||||
* changes differently.
|
||||
*/
|
||||
RECEIVING_PROPS: null
|
||||
});
|
||||
|
||||
module.exports = ReactLifeCycle;
|
||||
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Copyright 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 ReactUpdateQueue
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactLifeCycle = require('ReactLifeCycle');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var invariant = require('invariant');
|
||||
|
||||
function enqueueUpdate(internalInstance) {
|
||||
if (internalInstance._compositeLifeCycleState !== ReactLifeCycle.MOUNTING) {
|
||||
// If we're in a componentWillMount handler, don't enqueue a rerender
|
||||
// because ReactUpdates assumes we're in a browser context (which is
|
||||
// wrong for server rendering) and we're about to do a render anyway.
|
||||
// See bug in #1740.
|
||||
ReactUpdates.enqueueUpdate(internalInstance);
|
||||
}
|
||||
}
|
||||
|
||||
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
|
||||
invariant(
|
||||
ReactCurrentOwner.current == null,
|
||||
'%s(...): Cannot update during an existing state transition ' +
|
||||
'(such as within `render`). Render methods should be a pure function ' +
|
||||
'of props and state.',
|
||||
callerName
|
||||
);
|
||||
|
||||
var internalInstance = ReactInstanceMap.get(publicInstance);
|
||||
invariant(
|
||||
internalInstance,
|
||||
'%s(...): Can only update a mounted or mounting component. ' +
|
||||
'This usually means you called %s() on an unmounted ' +
|
||||
'component.',
|
||||
callerName,
|
||||
callerName
|
||||
);
|
||||
invariant(
|
||||
internalInstance._compositeLifeCycleState !==
|
||||
ReactLifeCycle.UNMOUNTING,
|
||||
'%s(...): Cannot call %s() on an unmounting component.',
|
||||
callerName,
|
||||
callerName
|
||||
);
|
||||
return internalInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* ReactUpdateQueue allows for state updates to be scheduled into a later
|
||||
* reconciliation step.
|
||||
*/
|
||||
var ReactUpdateQueue = {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @internal
|
||||
*/
|
||||
enqueueCallback: function(publicInstance, callback) {
|
||||
invariant(
|
||||
typeof callback === 'function',
|
||||
'enqueueCallback(...): You called `setProps`, `replaceProps`, ' +
|
||||
'`setState`, `replaceState`, or `forceUpdate` with a callback that ' +
|
||||
'isn\'t callable.'
|
||||
);
|
||||
var internalInstance = ReactInstanceMap.get(publicInstance);
|
||||
invariant(
|
||||
internalInstance,
|
||||
'Cannot enqueue a callback on an instance that is unmounted.'
|
||||
);
|
||||
if (internalInstance._compositeLifeCycleState === ReactLifeCycle.MOUNTING) {
|
||||
// Ignore callbacks in componentWillMount. See enqueueUpdate.
|
||||
return;
|
||||
}
|
||||
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.
|
||||
enqueueUpdate(internalInstance);
|
||||
},
|
||||
|
||||
enqueueCallbackInternal: function(internalInstance, callback) {
|
||||
invariant(
|
||||
typeof callback === "function",
|
||||
'enqueueCallback(...): You called `setProps`, `replaceProps`, ' +
|
||||
'`setState`, `replaceState`, or `forceUpdate` with a callback that ' +
|
||||
'isn\'t callable.'
|
||||
);
|
||||
if (internalInstance._pendingCallbacks) {
|
||||
internalInstance._pendingCallbacks.push(callback);
|
||||
} else {
|
||||
internalInstance._pendingCallbacks = [callback];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 `shouldUpdateComponent`, but it will invoke
|
||||
* `componentWillUpdate` and `componentDidUpdate`.
|
||||
*
|
||||
* @param {ReactClass} publicInstance The instance that should rerender.
|
||||
* @internal
|
||||
*/
|
||||
enqueueForceUpdate: function(publicInstance) {
|
||||
var internalInstance = getInternalInstanceReadyForUpdate(
|
||||
publicInstance,
|
||||
'forceUpdate'
|
||||
);
|
||||
|
||||
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) {
|
||||
var internalInstance = getInternalInstanceReadyForUpdate(
|
||||
publicInstance,
|
||||
'replaceState'
|
||||
);
|
||||
|
||||
internalInstance._pendingState = completeState;
|
||||
|
||||
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) {
|
||||
var internalInstance = getInternalInstanceReadyForUpdate(
|
||||
publicInstance,
|
||||
'setState'
|
||||
);
|
||||
|
||||
// Merge with `_pendingState` if it exists, otherwise with existing state.
|
||||
internalInstance._pendingState = assign(
|
||||
{},
|
||||
internalInstance._pendingState || internalInstance._instance.state,
|
||||
partialState
|
||||
);
|
||||
|
||||
enqueueUpdate(internalInstance);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a subset of the props.
|
||||
*
|
||||
* @param {ReactClass} publicInstance The instance that should rerender.
|
||||
* @param {object} partialProps Subset of the next props.
|
||||
* @internal
|
||||
*/
|
||||
enqueueSetProps: function(publicInstance, partialProps) {
|
||||
var internalInstance = getInternalInstanceReadyForUpdate(
|
||||
publicInstance,
|
||||
'setProps'
|
||||
);
|
||||
|
||||
invariant(
|
||||
internalInstance._isTopLevel,
|
||||
'setProps(...): You called `setProps` on a ' +
|
||||
'component with a parent. This is an anti-pattern since props will ' +
|
||||
'get reactively updated when rendered. Instead, change the owner\'s ' +
|
||||
'`render` method to pass the correct value as props to the component ' +
|
||||
'where it is created.'
|
||||
);
|
||||
|
||||
// Merge with the pending element if it exists, otherwise with existing
|
||||
// element props.
|
||||
var element = internalInstance._pendingElement ||
|
||||
internalInstance._currentElement;
|
||||
var props = assign({}, element.props, partialProps);
|
||||
internalInstance._pendingElement = ReactElement.cloneAndReplaceProps(
|
||||
element,
|
||||
props
|
||||
);
|
||||
|
||||
enqueueUpdate(internalInstance);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces all of the props.
|
||||
*
|
||||
* @param {ReactClass} publicInstance The instance that should rerender.
|
||||
* @param {object} props New props.
|
||||
* @internal
|
||||
*/
|
||||
enqueueReplaceProps: function(publicInstance, props) {
|
||||
var internalInstance = getInternalInstanceReadyForUpdate(
|
||||
publicInstance,
|
||||
'replaceProps'
|
||||
);
|
||||
|
||||
invariant(
|
||||
internalInstance._isTopLevel,
|
||||
'replaceProps(...): You called `replaceProps` on a ' +
|
||||
'component with a parent. This is an anti-pattern since props will ' +
|
||||
'get reactively updated when rendered. Instead, change the owner\'s ' +
|
||||
'`render` method to pass the correct value as props to the component ' +
|
||||
'where it is created.'
|
||||
);
|
||||
|
||||
// Merge with the pending element if it exists, otherwise with existing
|
||||
// element props.
|
||||
var element = internalInstance._pendingElement ||
|
||||
internalInstance._currentElement;
|
||||
internalInstance._pendingElement = ReactElement.cloneAndReplaceProps(
|
||||
element,
|
||||
props
|
||||
);
|
||||
|
||||
enqueueUpdate(internalInstance);
|
||||
},
|
||||
|
||||
enqueueElementInternal: function(internalInstance, newElement) {
|
||||
internalInstance._pendingElement = newElement;
|
||||
enqueueUpdate(internalInstance);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactUpdateQueue;
|
||||
@@ -190,13 +190,7 @@ flushBatchedUpdates = ReactPerf.measure(
|
||||
* 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, callback) {
|
||||
invariant(
|
||||
!callback || typeof callback === 'function',
|
||||
'enqueueUpdate(...): You called `setProps`, `replaceProps`, ' +
|
||||
'`setState`, `replaceState`, or `forceUpdate` with a callback that ' +
|
||||
'isn\'t callable.'
|
||||
);
|
||||
function enqueueUpdate(component) {
|
||||
ensureInjected();
|
||||
|
||||
// Various parts of our code (such as ReactCompositeComponent's
|
||||
@@ -213,19 +207,11 @@ function enqueueUpdate(component, callback) {
|
||||
);
|
||||
|
||||
if (!batchingStrategy.isBatchingUpdates) {
|
||||
batchingStrategy.batchedUpdates(enqueueUpdate, component, callback);
|
||||
batchingStrategy.batchedUpdates(enqueueUpdate, component);
|
||||
return;
|
||||
}
|
||||
|
||||
dirtyComponents.push(component);
|
||||
|
||||
if (callback) {
|
||||
if (component._pendingCallbacks) {
|
||||
component._pendingCallbacks.push(callback);
|
||||
} else {
|
||||
component._pendingCallbacks = [callback];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('ReactComponentLifeCycle', function() {
|
||||
React = require('React');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
ReactCompositeComponent = require('ReactCompositeComponent');
|
||||
CompositeComponentLifeCycle = ReactCompositeComponent.LifeCycle;
|
||||
CompositeComponentLifeCycle = require('ReactLifeCycle');
|
||||
|
||||
ReactInstanceMap = require('ReactInstanceMap');
|
||||
|
||||
@@ -403,11 +403,11 @@ describe('ReactComponentLifeCycle', function() {
|
||||
expect(function() {
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
}).toThrow(
|
||||
'Invariant Violation: replaceProps(...): You called `setProps` or ' +
|
||||
'`replaceProps` on a component with a parent. This is an anti-pattern ' +
|
||||
'since props will get reactively updated when rendered. Instead, ' +
|
||||
'change the owner\'s `render` method to pass the correct value as ' +
|
||||
'props to the component where it is created.'
|
||||
'Invariant Violation: setProps(...): You called `setProps` on a ' +
|
||||
'component with a parent. This is an anti-pattern since props will get ' +
|
||||
'reactively updated when rendered. Instead, change the owner\'s ' +
|
||||
'`render` method to pass the correct value as props to the component ' +
|
||||
'where it is created.'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -294,9 +294,9 @@ describe('ReactCompositeComponent', function() {
|
||||
expect(function() {
|
||||
instance.forceUpdate();
|
||||
}).toThrow(
|
||||
'Invariant Violation: forceUpdate(...): Can only force an update on ' +
|
||||
'mounted or mounting components. This usually means you called ' +
|
||||
'forceUpdate() on an unmounted component.'
|
||||
'Invariant Violation: forceUpdate(...): Can only update a mounted or ' +
|
||||
'mounting component. This usually means you called forceUpdate() on ' +
|
||||
'an unmounted component.'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -406,11 +406,11 @@ describe('ReactCompositeComponent', function() {
|
||||
expect(function() {
|
||||
innerInstance.setProps({value: 1});
|
||||
}).toThrow(
|
||||
'Invariant Violation: replaceProps(...): You called `setProps` or ' +
|
||||
'`replaceProps` on a component with a parent. This is an anti-pattern ' +
|
||||
'since props will get reactively updated when rendered. Instead, ' +
|
||||
'change the owner\'s `render` method to pass the correct value as ' +
|
||||
'props to the component where it is created.'
|
||||
'Invariant Violation: setProps(...): You called `setProps` on a ' +
|
||||
'component with a parent. This is an anti-pattern since props will get ' +
|
||||
'reactively updated when rendered. Instead, change the owner\'s ' +
|
||||
'`render` method to pass the correct value as props to the component ' +
|
||||
'where it is created.'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ describe('ReactCompositeComponent-state', function() {
|
||||
[ 'render', 'orange', null ],
|
||||
[ 'componentDidMount-start', 'orange', null ],
|
||||
// setState-sunrise and setState-orange should be called here,
|
||||
// after the bug in #
|
||||
// after the bug in #1740
|
||||
// componentDidMount() called setState({color:'yellow'}), currently this
|
||||
// occurs inline.
|
||||
// In a future where setState() is async, this test result will change.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
@@ -53,17 +53,10 @@ ReactComponentBase.prototype.setState = function(partialState, callback) {
|
||||
'instead, use forceUpdate().'
|
||||
);
|
||||
}
|
||||
|
||||
var internalInstance = ReactInstanceMap.get(this);
|
||||
invariant(
|
||||
internalInstance,
|
||||
'setState(...): Can only update a mounted or mounting component. ' +
|
||||
'This usually means you called setState() on an unmounted ' +
|
||||
'component.'
|
||||
);
|
||||
internalInstance.setState(
|
||||
partialState, callback
|
||||
);
|
||||
ReactUpdateQueue.enqueueSetState(this, partialState);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -81,14 +74,10 @@ ReactComponentBase.prototype.setState = function(partialState, callback) {
|
||||
* @protected
|
||||
*/
|
||||
ReactComponentBase.prototype.forceUpdate = function(callback) {
|
||||
var internalInstance = ReactInstanceMap.get(this);
|
||||
invariant(
|
||||
internalInstance,
|
||||
'forceUpdate(...): Can only force an update on mounted or mounting ' +
|
||||
'components. This usually means you called forceUpdate() on an ' +
|
||||
'unmounted component.'
|
||||
);
|
||||
internalInstance.forceUpdate(callback);
|
||||
ReactUpdateQueue.enqueueForceUpdate(this);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user