mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Inject the update queue into classes
This decouples the stateful imperative API from the class creation. Instead, they get injected into the class from the renderer. Stateful modules should always be injected. As a convenience, just like props/context/refs, we set it up after construction using mutation. That way it is optional to pass it along the super call constructor chain.
This commit is contained in:
@@ -16,9 +16,10 @@ var ReactElement = require('ReactElement');
|
||||
var ReactErrorUtils = require('ReactErrorUtils');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var emptyObject = require('emptyObject');
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
var keyOf = require('keyOf');
|
||||
@@ -712,9 +713,9 @@ var ReactClassMixin = {
|
||||
* type signature and the only use case for this, is to avoid that.
|
||||
*/
|
||||
replaceState: function(newState, callback) {
|
||||
ReactUpdateQueue.enqueueReplaceState(this, newState);
|
||||
this.updater.enqueueReplaceState(this, newState);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
this.updater.enqueueCallback(this, callback);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -725,7 +726,7 @@ var ReactClassMixin = {
|
||||
* @final
|
||||
*/
|
||||
isMounted: function() {
|
||||
return ReactUpdateQueue.isMounted(this);
|
||||
return this.updater.isMounted(this);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -738,9 +739,9 @@ var ReactClassMixin = {
|
||||
* @deprecated
|
||||
*/
|
||||
setProps: function(partialProps, callback) {
|
||||
ReactUpdateQueue.enqueueSetProps(this, partialProps);
|
||||
this.updater.enqueueSetProps(this, partialProps);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
this.updater.enqueueCallback(this, callback);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -754,9 +755,9 @@ var ReactClassMixin = {
|
||||
* @deprecated
|
||||
*/
|
||||
replaceProps: function(newProps, callback) {
|
||||
ReactUpdateQueue.enqueueReplaceProps(this, newProps);
|
||||
this.updater.enqueueReplaceProps(this, newProps);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
this.updater.enqueueCallback(this, callback);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -783,7 +784,7 @@ var ReactClass = {
|
||||
* @public
|
||||
*/
|
||||
createClass: function(spec) {
|
||||
var Constructor = function(props, context) {
|
||||
var Constructor = function(props, context, updater) {
|
||||
// This constructor is overridden by mocks. The argument is used
|
||||
// by mocks to assert on what gets mounted.
|
||||
|
||||
@@ -802,6 +803,9 @@ var ReactClass = {
|
||||
|
||||
this.props = props;
|
||||
this.context = context;
|
||||
this.refs = emptyObject;
|
||||
this.updater = updater || ReactNoopUpdateQueue;
|
||||
|
||||
this.state = null;
|
||||
|
||||
// ReactClasses doesn't have constructors. Instead, they use the
|
||||
|
||||
@@ -11,17 +11,22 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
|
||||
/**
|
||||
* Base class helpers for the updating state of a component.
|
||||
*/
|
||||
function ReactComponent(props, context) {
|
||||
function ReactComponent(props, context, updater) {
|
||||
this.props = props;
|
||||
this.context = context;
|
||||
this.refs = emptyObject;
|
||||
// We initialize the default updater but the real one gets injected by the
|
||||
// renderer.
|
||||
this.updater = updater || ReactNoopUpdateQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,9 +69,9 @@ ReactComponent.prototype.setState = function(partialState, callback) {
|
||||
'instead, use forceUpdate().'
|
||||
);
|
||||
}
|
||||
ReactUpdateQueue.enqueueSetState(this, partialState);
|
||||
this.updater.enqueueSetState(this, partialState);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
this.updater.enqueueCallback(this, callback);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,9 +90,9 @@ ReactComponent.prototype.setState = function(partialState, callback) {
|
||||
* @protected
|
||||
*/
|
||||
ReactComponent.prototype.forceUpdate = function(callback) {
|
||||
ReactUpdateQueue.enqueueForceUpdate(this);
|
||||
this.updater.enqueueForceUpdate(this);
|
||||
if (callback) {
|
||||
ReactUpdateQueue.enqueueCallback(this, callback);
|
||||
this.updater.enqueueCallback(this, callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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 ReactNoopUpdateQueue
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
function warnTDZ(publicInstance, callerName) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'%s(...): Can only update a mounted or mounting component. ' +
|
||||
'This usually means you called %s() on an unmounted component. ' +
|
||||
'This is a no-op. Please check the code for the %s component.',
|
||||
callerName,
|
||||
callerName,
|
||||
publicInstance.constructor && publicInstance.constructor.displayName || ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the abstract API for an update queue.
|
||||
*/
|
||||
var ReactNoopUpdateQueue = {
|
||||
|
||||
/**
|
||||
* Checks whether or not this composite component is mounted.
|
||||
* @param {ReactClass} publicInstance The instance we want to test.
|
||||
* @return {boolean} True if mounted, false otherwise.
|
||||
* @protected
|
||||
* @final
|
||||
*/
|
||||
isMounted: function(publicInstance) {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) { },
|
||||
|
||||
/**
|
||||
* Forces an update. This should only be invoked when it is known with
|
||||
* certainty that we are **not** in a DOM transaction.
|
||||
*
|
||||
* You may want to call this when you know that some deeper aspect of the
|
||||
* component's state has changed but `setState` was not called.
|
||||
*
|
||||
* This will not invoke `shouldComponentUpdate`, but it will invoke
|
||||
* `componentWillUpdate` and `componentDidUpdate`.
|
||||
*
|
||||
* @param {ReactClass} publicInstance The instance that should rerender.
|
||||
* @internal
|
||||
*/
|
||||
enqueueForceUpdate: function(publicInstance) {
|
||||
warnTDZ(publicInstance, 'forceUpdate');
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
warnTDZ(publicInstance, 'replaceState');
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
warnTDZ(publicInstance, 'setState');
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
warnTDZ(publicInstance, 'setProps');
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces all of the props.
|
||||
*
|
||||
* @param {ReactClass} publicInstance The instance that should rerender.
|
||||
* @param {object} props New props.
|
||||
* @internal
|
||||
*/
|
||||
enqueueReplaceProps: function(publicInstance, props) {
|
||||
warnTDZ(publicInstance, 'replaceProps');
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactNoopUpdateQueue;
|
||||
@@ -22,6 +22,7 @@ var ReactPerf = require('ReactPerf');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
var ReactReconciler = require('ReactReconciler');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var emptyObject = require('emptyObject');
|
||||
@@ -130,7 +131,7 @@ var ReactCompositeComponentMixin = {
|
||||
);
|
||||
|
||||
// Initialize the public class
|
||||
var inst = new Component(publicProps, publicContext);
|
||||
var inst = new Component(publicProps, publicContext, ReactUpdateQueue);
|
||||
|
||||
if (__DEV__) {
|
||||
// This will throw later in _renderValidatedComponent, but add an early
|
||||
@@ -150,6 +151,7 @@ var ReactCompositeComponentMixin = {
|
||||
inst.props = publicProps;
|
||||
inst.context = publicContext;
|
||||
inst.refs = emptyObject;
|
||||
inst.updater = ReactUpdateQueue;
|
||||
|
||||
this._instance = inst;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user