mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Extract setState, setProps etc into ReactUpdateQueue
I originally did this work so that we could allow setState to be called before the internal ReactCompositeComponent was constructed. It's unlikely that we'll go down that route now but this still seems like a better abstraction. It communicates that this is not immediately updating an object oriented class. It's just a queue which a minor optimization. It also avoids bloating the ReactCompositeComponent file. Since they both depend on the life cycle I break that out into a common shared dependency. In a follow up I'll refactor the life-cycle management.
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