Merge pull request #2930 from sebmarkbage/updatequeue

Extract setState, setProps etc into ReactUpdateQueue
This commit is contained in:
Sebastian Markbåge
2015-01-27 00:57:44 -08:00
10 changed files with 377 additions and 279 deletions
+5 -2
View File
@@ -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__) {
+13 -24
View File
@@ -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);
}
}
};
+9 -202
View File
@@ -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
};
+60
View File
@@ -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;
+264
View File
@@ -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;
+2 -16
View File
@@ -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.
+9 -20
View File
@@ -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);
}
};
/**