mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Added warning if owner-based and parent-based contexts differ.
This commit is contained in:
@@ -17,14 +17,17 @@ var ReactMarkupChecksum = require('ReactMarkupChecksum');
|
||||
var ReactServerRenderingTransaction =
|
||||
require('ReactServerRenderingTransaction');
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
var instantiateReactComponent = require('instantiateReactComponent');
|
||||
var invariant = require('invariant');
|
||||
|
||||
/**
|
||||
* @param {ReactElement} element
|
||||
* @param {?object} context
|
||||
* @return {string} the HTML markup
|
||||
*/
|
||||
function renderToString(element) {
|
||||
function renderToString(element, context) {
|
||||
if(context === undefined) context = emptyObject;
|
||||
invariant(
|
||||
ReactElement.isValidElement(element),
|
||||
'renderToString(): You must pass a valid ReactElement.'
|
||||
@@ -37,7 +40,7 @@ function renderToString(element) {
|
||||
|
||||
return transaction.perform(function() {
|
||||
var componentInstance = instantiateReactComponent(element, null);
|
||||
var markup = componentInstance.mountComponent(id, transaction, 0);
|
||||
var markup = componentInstance.mountComponent(id, transaction, 0, context);
|
||||
return ReactMarkupChecksum.addChecksumToMarkup(markup);
|
||||
}, null);
|
||||
} finally {
|
||||
@@ -47,10 +50,12 @@ function renderToString(element) {
|
||||
|
||||
/**
|
||||
* @param {ReactElement} element
|
||||
* @param {?object} context
|
||||
* @return {string} the HTML markup, without the extra React ID and checksum
|
||||
* (for generating static pages)
|
||||
*/
|
||||
function renderToStaticMarkup(element) {
|
||||
function renderToStaticMarkup(element, context) {
|
||||
if(context === undefined) context = emptyObject;
|
||||
invariant(
|
||||
ReactElement.isValidElement(element),
|
||||
'renderToStaticMarkup(): You must pass a valid ReactElement.'
|
||||
@@ -63,7 +68,7 @@ function renderToStaticMarkup(element) {
|
||||
|
||||
return transaction.perform(function() {
|
||||
var componentInstance = instantiateReactComponent(element, null);
|
||||
return componentInstance.mountComponent(id, transaction, 0);
|
||||
return componentInstance.mountComponent(id, transaction, 0, context);
|
||||
}, null);
|
||||
} finally {
|
||||
ReactServerRenderingTransaction.release(transaction);
|
||||
|
||||
@@ -168,19 +168,21 @@ ReactDOMComponent.Mixin = {
|
||||
mountComponent: ReactPerf.measure(
|
||||
'ReactDOMComponent',
|
||||
'mountComponent',
|
||||
function(rootID, transaction, mountDepth) {
|
||||
function(rootID, transaction, mountDepth, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
ReactComponent.Mixin.mountComponent.call(
|
||||
this,
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth
|
||||
mountDepth,
|
||||
context
|
||||
);
|
||||
this._previousStyleCopy = null;
|
||||
assertValidProps(this._currentElement.props);
|
||||
var closeTag = omittedCloseTags[this._tag] ? '' : '</' + this._tag + '>';
|
||||
return (
|
||||
this._createOpenTagMarkupAndPutListeners(transaction) +
|
||||
this._createContentMarkup(transaction) +
|
||||
this._createContentMarkup(transaction, context) +
|
||||
closeTag
|
||||
);
|
||||
}
|
||||
@@ -242,9 +244,10 @@ ReactDOMComponent.Mixin = {
|
||||
*
|
||||
* @private
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {object} context
|
||||
* @return {string} Content markup.
|
||||
*/
|
||||
_createContentMarkup: function(transaction) {
|
||||
_createContentMarkup: function(transaction, context) {
|
||||
var prefix = '';
|
||||
if (this._tag === 'listing' ||
|
||||
this._tag === 'pre' ||
|
||||
@@ -272,7 +275,8 @@ ReactDOMComponent.Mixin = {
|
||||
} else if (childrenToUse != null) {
|
||||
var mountImages = this.mountChildren(
|
||||
childrenToUse,
|
||||
transaction
|
||||
transaction,
|
||||
context
|
||||
);
|
||||
return prefix + mountImages.join('');
|
||||
}
|
||||
@@ -280,7 +284,8 @@ ReactDOMComponent.Mixin = {
|
||||
return prefix;
|
||||
},
|
||||
|
||||
receiveComponent: function(nextElement, transaction) {
|
||||
receiveComponent: function(nextElement, transaction, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
if (nextElement === this._currentElement &&
|
||||
nextElement._owner != null) {
|
||||
// Since elements are immutable after the owner is rendered,
|
||||
@@ -295,7 +300,7 @@ ReactDOMComponent.Mixin = {
|
||||
|
||||
var prevElement = this._currentElement;
|
||||
this._currentElement = nextElement;
|
||||
this.updateComponent(transaction, prevElement, nextElement);
|
||||
this.updateComponent(transaction, prevElement, nextElement, context);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -311,16 +316,19 @@ ReactDOMComponent.Mixin = {
|
||||
updateComponent: ReactPerf.measure(
|
||||
'ReactDOMComponent',
|
||||
'updateComponent',
|
||||
function(transaction, prevElement, nextElement) {
|
||||
function(transaction, prevElement, nextElement, context) {
|
||||
if(context === undefined) throw new Error("Context required for mounting");
|
||||
if(context === null) throw new Error("Assert: context is not null");
|
||||
assertValidProps(this._currentElement.props);
|
||||
ReactComponent.Mixin.updateComponent.call(
|
||||
this,
|
||||
transaction,
|
||||
prevElement,
|
||||
nextElement
|
||||
nextElement,
|
||||
context
|
||||
);
|
||||
this._updateDOMProperties(prevElement.props, transaction);
|
||||
this._updateDOMChildren(prevElement.props, transaction);
|
||||
this._updateDOMChildren(prevElement.props, transaction, context);
|
||||
}
|
||||
),
|
||||
|
||||
@@ -428,7 +436,8 @@ ReactDOMComponent.Mixin = {
|
||||
* @param {object} lastProps
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
*/
|
||||
_updateDOMChildren: function(lastProps, transaction) {
|
||||
_updateDOMChildren: function(lastProps, transaction, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
var nextProps = this._currentElement.props;
|
||||
|
||||
var lastContent =
|
||||
@@ -452,7 +461,7 @@ ReactDOMComponent.Mixin = {
|
||||
var lastHasContentOrHtml = lastContent != null || lastHtml != null;
|
||||
var nextHasContentOrHtml = nextContent != null || nextHtml != null;
|
||||
if (lastChildren != null && nextChildren == null) {
|
||||
this.updateChildren(null, transaction);
|
||||
this.updateChildren(null, transaction, context);
|
||||
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
|
||||
this.updateTextContent('');
|
||||
}
|
||||
@@ -469,7 +478,7 @@ ReactDOMComponent.Mixin = {
|
||||
);
|
||||
}
|
||||
} else if (nextChildren != null) {
|
||||
this.updateChildren(nextChildren, transaction);
|
||||
this.updateChildren(nextChildren, transaction, context);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ assign(ReactDOMTextComponent.prototype, {
|
||||
* @return {string} Markup for this text node.
|
||||
* @internal
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth) {
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
this._rootNodeID = rootID;
|
||||
var escapedText = escapeTextForBrowser(this._stringText);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ var ReactMarkupChecksum = require('ReactMarkupChecksum');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
var containsNode = require('containsNode');
|
||||
var deprecated = require('deprecated');
|
||||
var getReactRootElementInContainer = require('getReactRootElementInContainer');
|
||||
@@ -224,7 +225,7 @@ function mountComponentIntoNode(
|
||||
container,
|
||||
transaction,
|
||||
shouldReuseMarkup) {
|
||||
var markup = this.mountComponent(rootID, transaction, 0);
|
||||
var markup = this.mountComponent(rootID, transaction, 0, emptyObject);
|
||||
ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup);
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ describe('ReactDOMComponent', function() {
|
||||
|
||||
genMarkup = function(props) {
|
||||
var transaction = new ReactReconcileTransaction();
|
||||
return (new NodeStub(props))._createContentMarkup(transaction);
|
||||
return (new NodeStub(props))._createContentMarkup(transaction, {});
|
||||
};
|
||||
|
||||
this.addMatchers({
|
||||
@@ -328,7 +328,7 @@ describe('ReactDOMComponent', function() {
|
||||
_owner: null,
|
||||
_context: null
|
||||
});
|
||||
return stubComponent.mountComponent('test', transaction, 0);
|
||||
return stubComponent.mountComponent('test', transaction, 0, {});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('Danger', function() {
|
||||
it('should render markup', function() {
|
||||
var markup = instantiateReactComponent(
|
||||
<div />
|
||||
).mountComponent('.rX', transaction, 0);
|
||||
).mountComponent('.rX', transaction, 0, {});
|
||||
var output = Danger.dangerouslyRenderMarkup([markup])[0];
|
||||
|
||||
expect(output.nodeName).toBe('DIV');
|
||||
@@ -44,7 +44,7 @@ describe('Danger', function() {
|
||||
).mountComponent(
|
||||
'.rX',
|
||||
transaction,
|
||||
0
|
||||
0, {}
|
||||
);
|
||||
var output = Danger.dangerouslyRenderMarkup([markup])[0];
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('Danger', function() {
|
||||
it('should render wrapped markup', function() {
|
||||
var markup = instantiateReactComponent(
|
||||
<th />
|
||||
).mountComponent('.rX', transaction, 0);
|
||||
).mountComponent('.rX', transaction, 0, {});
|
||||
var output = Danger.dangerouslyRenderMarkup([markup])[0];
|
||||
|
||||
expect(output.nodeName).toBe('TH');
|
||||
|
||||
@@ -114,7 +114,6 @@ var ReactComponent = {
|
||||
// We keep the old element and a reference to the pending element
|
||||
// to track updates.
|
||||
this._currentElement = element;
|
||||
|
||||
this._rootNodeID = null;
|
||||
this._mountIndex = 0;
|
||||
this._mountDepth = 0;
|
||||
@@ -134,7 +133,8 @@ var ReactComponent = {
|
||||
* @return {?string} Rendered markup to be inserted into the DOM.
|
||||
* @internal
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth) {
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
var ref = this._currentElement.ref;
|
||||
if (ref != null) {
|
||||
var owner = this._currentElement._owner;
|
||||
@@ -173,7 +173,8 @@ var ReactComponent = {
|
||||
* @param {object} nextElement
|
||||
* @internal
|
||||
*/
|
||||
updateComponent: function(transaction, prevElement, nextElement) {
|
||||
updateComponent: function(transaction, prevElement, nextElement, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
// If either the owner or a `ref` has changed, make sure the newest owner
|
||||
// has stored a reference to `this`, and the previous owner (if different)
|
||||
// has forgotten the reference to `this`. We use the element instead
|
||||
|
||||
@@ -20,10 +20,12 @@ var ReactPerf = require('ReactPerf');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
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');
|
||||
|
||||
@@ -116,6 +118,7 @@ var ReactCompositeComponentMixin = assign({},
|
||||
this._instance.refs = emptyObject;
|
||||
|
||||
this._pendingElement = null;
|
||||
this._pendingContext = null;
|
||||
this._pendingState = null;
|
||||
this._pendingForceUpdate = false;
|
||||
this._compositeLifeCycleState = null;
|
||||
@@ -125,6 +128,8 @@ var ReactCompositeComponentMixin = assign({},
|
||||
// Children can be either an array or more than one argument
|
||||
ReactComponent.Mixin.construct.apply(this, arguments);
|
||||
|
||||
this._context = null;
|
||||
|
||||
// See ReactUpdates.
|
||||
this._pendingCallbacks = null;
|
||||
},
|
||||
@@ -152,14 +157,18 @@ var ReactCompositeComponentMixin = assign({},
|
||||
mountComponent: ReactPerf.measure(
|
||||
'ReactCompositeComponent',
|
||||
'mountComponent',
|
||||
function(rootID, transaction, mountDepth) {
|
||||
function(rootID, transaction, mountDepth, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
ReactComponent.Mixin.mountComponent.call(
|
||||
this,
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth
|
||||
mountDepth,
|
||||
context
|
||||
);
|
||||
|
||||
this._context = context;
|
||||
|
||||
var inst = this._instance;
|
||||
|
||||
// Store a reference from the instance back to the internal representation
|
||||
@@ -168,6 +177,9 @@ var ReactCompositeComponentMixin = assign({},
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
|
||||
|
||||
inst.context = this._processContext(this._currentElement._context);
|
||||
if (__DEV__) {
|
||||
this._warnIfContextsDiffer(this._currentElement._context, context);
|
||||
}
|
||||
inst.props = this._processProps(this._currentElement.props);
|
||||
|
||||
var initialState = inst.getInitialState ? inst.getInitialState() : null;
|
||||
@@ -211,7 +223,8 @@ var ReactCompositeComponentMixin = assign({},
|
||||
var markup = this._renderedComponent.mountComponent(
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth + 1
|
||||
mountDepth + 1,
|
||||
this._processChildContext(context)
|
||||
);
|
||||
if (inst.componentDidMount) {
|
||||
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
|
||||
@@ -246,6 +259,8 @@ var ReactCompositeComponentMixin = assign({},
|
||||
|
||||
ReactComponent.Mixin.unmountComponent.call(this);
|
||||
|
||||
this._context = null;
|
||||
|
||||
// Delete the reference from the instance to this internal representation
|
||||
// which allow the internals to be properly cleaned up even if the user
|
||||
// leaks a reference to the public instance.
|
||||
@@ -403,18 +418,17 @@ var ReactCompositeComponentMixin = assign({},
|
||||
_processContext: function(context) {
|
||||
var maskedContext = null;
|
||||
var contextTypes = this._instance.constructor.contextTypes;
|
||||
if (contextTypes) {
|
||||
maskedContext = {};
|
||||
for (var contextName in contextTypes) {
|
||||
maskedContext[contextName] = context[contextName];
|
||||
}
|
||||
if (__DEV__) {
|
||||
this._checkPropTypes(
|
||||
contextTypes,
|
||||
maskedContext,
|
||||
ReactPropTypeLocations.context
|
||||
);
|
||||
}
|
||||
if (!contextTypes) return emptyObject;
|
||||
maskedContext = {};
|
||||
for (var contextName in contextTypes) {
|
||||
maskedContext[contextName] = context[contextName];
|
||||
}
|
||||
if (__DEV__) {
|
||||
this._checkPropTypes(
|
||||
contextTypes,
|
||||
maskedContext,
|
||||
ReactPropTypeLocations.context
|
||||
);
|
||||
}
|
||||
return maskedContext;
|
||||
},
|
||||
@@ -502,7 +516,8 @@ var ReactCompositeComponentMixin = assign({},
|
||||
}
|
||||
},
|
||||
|
||||
receiveComponent: function(nextElement, transaction) {
|
||||
receiveComponent: function(nextElement, transaction, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
if (nextElement === this._currentElement &&
|
||||
nextElement._owner != null) {
|
||||
// Since elements are immutable after the owner is rendered,
|
||||
@@ -516,6 +531,7 @@ var ReactCompositeComponentMixin = assign({},
|
||||
}
|
||||
|
||||
this._pendingElement = nextElement;
|
||||
this._pendingContext = context;
|
||||
this.performUpdateIfNecessary(transaction);
|
||||
},
|
||||
|
||||
@@ -537,6 +553,7 @@ var ReactCompositeComponentMixin = assign({},
|
||||
|
||||
if (this._pendingElement == null &&
|
||||
this._pendingState == null &&
|
||||
this._pendingContext == null &&
|
||||
!this._pendingForceUpdate) {
|
||||
return;
|
||||
}
|
||||
@@ -548,13 +565,46 @@ var ReactCompositeComponentMixin = assign({},
|
||||
this._pendingElement = null;
|
||||
}
|
||||
|
||||
var prevContext = this._context;
|
||||
var nextContext = prevContext;
|
||||
if (this._pendingContext != null) {
|
||||
nextContext = this._pendingContext;
|
||||
this._pendingContext = null;
|
||||
}
|
||||
|
||||
this.updateComponent(
|
||||
transaction,
|
||||
prevElement,
|
||||
nextElement
|
||||
nextElement,
|
||||
prevContext,
|
||||
nextContext
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Compare two contexts, warning if they are different
|
||||
* TODO: Remove this check when owner-context is removed
|
||||
*/
|
||||
_warnIfContextsDiffer: function(ownerBasedContext, parentBasedContext) {
|
||||
invariant(ownerBasedContext !== undefined, "Owner based context is required parameter");
|
||||
invariant(parentBasedContext !== undefined, "Parent based ontext is required parameter");
|
||||
var ownerKeys = Object.keys(ownerBasedContext).sort();
|
||||
var parentKeys = Object.keys(parentBasedContext).sort();
|
||||
if (ownerKeys.length != parentKeys.length || ownerKeys.toString() != parentKeys.toString()) {
|
||||
var message = ("owner based context (keys: " +
|
||||
Object.keys(ownerBasedContext) + ") does not equal parent based" +
|
||||
" context (keys: "+Object.keys(parentBasedContext)+")" +
|
||||
" while mounting " +
|
||||
(this._instance.constructor.displayName || 'ReactCompositeComponent'));
|
||||
console.warn(message);
|
||||
monitorCodeUse('contexts_differ', {
|
||||
ownerBasedKeys: Object.keys(ownerBasedContext),
|
||||
parentBasedKeys: Object.keys(parentBasedContext),
|
||||
mounting: (this._instance.constructor.displayName || 'ReactCompositeComponent')
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform an update to a mounted component. The componentWillReceiveProps and
|
||||
* shouldComponentUpdate methods are called, then (assuming the update isn't
|
||||
@@ -573,13 +623,20 @@ var ReactCompositeComponentMixin = assign({},
|
||||
updateComponent: ReactPerf.measure(
|
||||
'ReactCompositeComponent',
|
||||
'updateComponent',
|
||||
function(transaction, prevParentElement, nextParentElement) {
|
||||
function(
|
||||
transaction,
|
||||
prevParentElement,
|
||||
nextParentElement,
|
||||
prevUnmaskedContext,
|
||||
nextUnmaskedContext) {
|
||||
// Update refs regardless of what shouldComponentUpdate returns
|
||||
ReactComponent.Mixin.updateComponent.call(
|
||||
this,
|
||||
transaction,
|
||||
prevParentElement,
|
||||
nextParentElement
|
||||
nextParentElement,
|
||||
prevUnmaskedContext,
|
||||
nextUnmaskedContext
|
||||
);
|
||||
|
||||
var inst = this._instance;
|
||||
@@ -623,6 +680,7 @@ var ReactCompositeComponentMixin = assign({},
|
||||
// If it's determined that a component should not update, we still want
|
||||
// to set props and state but we shortcut the rest of the update.
|
||||
this._currentElement = nextParentElement;
|
||||
this._context = nextUnmaskedContext;
|
||||
inst.props = nextProps;
|
||||
inst.state = nextState;
|
||||
inst.context = nextContext;
|
||||
@@ -636,7 +694,8 @@ var ReactCompositeComponentMixin = assign({},
|
||||
nextProps,
|
||||
nextState,
|
||||
nextContext,
|
||||
transaction
|
||||
transaction,
|
||||
nextUnmaskedContext
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -649,6 +708,7 @@ var ReactCompositeComponentMixin = assign({},
|
||||
* @param {?object} nextState Next object to set as state.
|
||||
* @param {?object} nextContext Next public object to set as context.
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {?object} unmaskedContext
|
||||
* @private
|
||||
*/
|
||||
_performComponentUpdate: function(
|
||||
@@ -656,8 +716,11 @@ var ReactCompositeComponentMixin = assign({},
|
||||
nextProps,
|
||||
nextState,
|
||||
nextContext,
|
||||
transaction
|
||||
transaction,
|
||||
unmaskedContext
|
||||
) {
|
||||
invariant(unmaskedContext !== undefined, "Context required for mounting");
|
||||
invariant(unmaskedContext !== null, "Context must be non-null");
|
||||
var inst = this._instance;
|
||||
|
||||
var prevProps = inst.props;
|
||||
@@ -669,11 +732,12 @@ var ReactCompositeComponentMixin = assign({},
|
||||
}
|
||||
|
||||
this._currentElement = nextElement;
|
||||
this._context = unmaskedContext;
|
||||
inst.props = nextProps;
|
||||
inst.state = nextState;
|
||||
inst.context = nextContext;
|
||||
|
||||
this._updateRenderedComponent(transaction);
|
||||
this._updateRenderedComponent(transaction, nextContext);
|
||||
|
||||
if (inst.componentDidUpdate) {
|
||||
transaction.getReactMountReady().enqueue(
|
||||
@@ -689,14 +753,16 @@ var ReactCompositeComponentMixin = assign({},
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @internal
|
||||
*/
|
||||
_updateRenderedComponent: function(transaction) {
|
||||
_updateRenderedComponent: function(transaction, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
var prevComponentInstance = this._renderedComponent;
|
||||
var prevRenderedElement = prevComponentInstance._currentElement;
|
||||
var nextRenderedElement = this._renderValidatedComponent();
|
||||
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
|
||||
prevComponentInstance.receiveComponent(
|
||||
nextRenderedElement,
|
||||
transaction
|
||||
transaction,
|
||||
this._processChildContext(context)
|
||||
);
|
||||
} else {
|
||||
// These two IDs are actually the same! But nothing should rely on that.
|
||||
@@ -711,7 +777,8 @@ var ReactCompositeComponentMixin = assign({},
|
||||
var nextMarkup = this._renderedComponent.mountComponent(
|
||||
thisID,
|
||||
transaction,
|
||||
this._mountDepth + 1
|
||||
this._mountDepth + 1,
|
||||
context
|
||||
);
|
||||
ReactComponent.BackendIDOperations.dangerouslyReplaceNodeWithMarkupByID(
|
||||
prevComponentID,
|
||||
@@ -828,12 +895,13 @@ var ShallowMixin = assign({},
|
||||
* @final
|
||||
* @internal
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth) {
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
ReactComponent.Mixin.mountComponent.call(
|
||||
this,
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth
|
||||
mountDepth,
|
||||
context
|
||||
);
|
||||
|
||||
var inst = this._instance;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"use strict";
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var emptyObject = require('emptyObject');
|
||||
|
||||
/**
|
||||
* Keeps track of the current context.
|
||||
@@ -25,7 +26,7 @@ var ReactContext = {
|
||||
* @internal
|
||||
* @type {object}
|
||||
*/
|
||||
current: {},
|
||||
current: emptyObject,
|
||||
|
||||
/**
|
||||
* Temporarily extends the current context while executing scopedCallback.
|
||||
|
||||
@@ -17,6 +17,7 @@ var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
|
||||
var flattenChildren = require('flattenChildren');
|
||||
var instantiateReactComponent = require('instantiateReactComponent');
|
||||
var invariant = require('invariant');
|
||||
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
|
||||
|
||||
/**
|
||||
@@ -178,7 +179,8 @@ var ReactMultiChild = {
|
||||
* @return {array} An array of mounted representations.
|
||||
* @internal
|
||||
*/
|
||||
mountChildren: function(nestedChildren, transaction) {
|
||||
mountChildren: function(nestedChildren, transaction, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
var children = flattenChildren(nestedChildren);
|
||||
var mountImages = [];
|
||||
var index = 0;
|
||||
@@ -195,7 +197,8 @@ var ReactMultiChild = {
|
||||
var mountImage = childInstance.mountComponent(
|
||||
rootID,
|
||||
transaction,
|
||||
this._mountDepth + 1
|
||||
this._mountDepth + 1,
|
||||
context
|
||||
);
|
||||
childInstance._mountIndex = index;
|
||||
mountImages.push(mountImage);
|
||||
@@ -240,11 +243,12 @@ var ReactMultiChild = {
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @internal
|
||||
*/
|
||||
updateChildren: function(nextNestedChildren, transaction) {
|
||||
updateChildren: function(nextNestedChildren, transaction, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
updateDepth++;
|
||||
var errorThrown = true;
|
||||
try {
|
||||
this._updateChildren(nextNestedChildren, transaction);
|
||||
this._updateChildren(nextNestedChildren, transaction, context);
|
||||
errorThrown = false;
|
||||
} finally {
|
||||
updateDepth--;
|
||||
@@ -263,7 +267,8 @@ var ReactMultiChild = {
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
_updateChildren: function(nextNestedChildren, transaction) {
|
||||
_updateChildren: function(nextNestedChildren, transaction, context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
var nextChildren = flattenChildren(nextNestedChildren);
|
||||
var prevChildren = this._renderedChildren;
|
||||
if (!nextChildren && !prevChildren) {
|
||||
@@ -284,7 +289,7 @@ var ReactMultiChild = {
|
||||
if (shouldUpdateReactComponent(prevElement, nextElement)) {
|
||||
this.moveChild(prevChild, nextIndex, lastIndex);
|
||||
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
|
||||
prevChild.receiveComponent(nextElement, transaction);
|
||||
prevChild.receiveComponent(nextElement, transaction, context);
|
||||
prevChild._mountIndex = nextIndex;
|
||||
} else {
|
||||
if (prevChild) {
|
||||
@@ -298,7 +303,7 @@ var ReactMultiChild = {
|
||||
null
|
||||
);
|
||||
this._mountChildByNameAtIndex(
|
||||
nextChildInstance, name, nextIndex, transaction
|
||||
nextChildInstance, name, nextIndex, transaction, context
|
||||
);
|
||||
}
|
||||
nextIndex++;
|
||||
@@ -389,13 +394,20 @@ var ReactMultiChild = {
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @private
|
||||
*/
|
||||
_mountChildByNameAtIndex: function(child, name, index, transaction) {
|
||||
_mountChildByNameAtIndex: function(
|
||||
child,
|
||||
name,
|
||||
index,
|
||||
transaction,
|
||||
context) {
|
||||
invariant(context !== undefined, "Context is required parameter");
|
||||
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
|
||||
var rootID = this._rootNodeID + name;
|
||||
var mountImage = child.mountComponent(
|
||||
rootID,
|
||||
transaction,
|
||||
this._mountDepth + 1
|
||||
this._mountDepth + 1,
|
||||
context
|
||||
);
|
||||
child._mountIndex = index;
|
||||
this.createChild(child, mountImage);
|
||||
|
||||
@@ -140,6 +140,7 @@ function runBatchedUpdates(transaction) {
|
||||
// be here, but we assume that it has cleared its _pendingCallbacks and
|
||||
// that performUpdateIfNecessary is a noop.
|
||||
var component = dirtyComponents[i];
|
||||
|
||||
// If performUpdateIfNecessary happens to enqueue any new updates, we
|
||||
// shouldn't execute the callbacks until the next render happens, so
|
||||
// stash the callbacks first
|
||||
|
||||
@@ -961,6 +961,29 @@ describe('ReactCompositeComponent', function() {
|
||||
reactComponentExpect(grandchildInstance).scalarContextEqual({foo: 'bar', depth: 1});
|
||||
});
|
||||
|
||||
it('warn if contexts differ', function() {
|
||||
var Component = React.createClass({
|
||||
contextTypes: {
|
||||
foo: ReactPropTypes.string.isRequired
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
React.withContext({foo: 'bar'}, function() {
|
||||
ReactTestUtils.renderIntoDocument(<Component />);
|
||||
});
|
||||
|
||||
expect(console.warn.mock.calls.length).toBe(2);
|
||||
expect(console.warn.mock.calls[1][0]).toBe(
|
||||
'owner based context (keys: foo) does not equal parent based ' +
|
||||
'context (keys: ) while mounting ReactCompositeComponent'
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
it('should check context types', function() {
|
||||
var Component = React.createClass({
|
||||
contextTypes: {
|
||||
@@ -979,21 +1002,50 @@ describe('ReactCompositeComponent', function() {
|
||||
'Warning: Required context `foo` was not specified in `Component`.'
|
||||
);
|
||||
|
||||
React.withContext({foo: 'bar'}, function() {
|
||||
ReactTestUtils.renderIntoDocument(<Component />);
|
||||
var ComponentInFooStringContext = React.createClass({
|
||||
childContextTypes: {
|
||||
foo: ReactPropTypes.string
|
||||
},
|
||||
|
||||
getChildContext: function() {
|
||||
return {
|
||||
foo: this.props.fooValue
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <Component />;
|
||||
}
|
||||
});
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<ComponentInFooStringContext fooValue={'bar'} />);
|
||||
|
||||
// Previous call should not error
|
||||
expect(console.warn.mock.calls.length).toBe(1);
|
||||
|
||||
React.withContext({foo: 123}, function() {
|
||||
ReactTestUtils.renderIntoDocument(<Component />);
|
||||
var ComponentInFooNumberContext = React.createClass({
|
||||
childContextTypes: {
|
||||
foo: ReactPropTypes.number
|
||||
},
|
||||
|
||||
getChildContext: function() {
|
||||
return {
|
||||
foo: this.props.fooValue
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <Component />;
|
||||
}
|
||||
});
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<ComponentInFooNumberContext fooValue={123} />);
|
||||
|
||||
expect(console.warn.mock.calls.length).toBe(2);
|
||||
expect(console.warn.mock.calls[1][0]).toBe(
|
||||
'Warning: Invalid context `foo` of type `number` supplied ' +
|
||||
'to `Component`, expected `string`.'
|
||||
'to `Component`, expected `string`.' +
|
||||
' Check the render method of `ComponentInFooNumberContext`.'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1014,16 +1066,18 @@ describe('ReactCompositeComponent', function() {
|
||||
});
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />);
|
||||
|
||||
expect(console.warn.mock.calls.length).toBe(1);
|
||||
expect(console.warn.mock.calls.length).toBe(2);
|
||||
expect(console.warn.mock.calls[0][0]).toBe(
|
||||
'Warning: Required child context `foo` was not specified in `Component`.'
|
||||
);
|
||||
expect(console.warn.mock.calls[1][0]).toBe(
|
||||
'Warning: Required child context `foo` was not specified in `Component`.'
|
||||
);
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 123}} />);
|
||||
|
||||
expect(console.warn.mock.calls.length).toBe(2);
|
||||
expect(console.warn.mock.calls[1][0]).toBe(
|
||||
expect(console.warn.mock.calls.length).toBe(4);
|
||||
expect(console.warn.mock.calls[3][0]).toBe(
|
||||
'Warning: Invalid child context `foo` of type `number` ' +
|
||||
'supplied to `Component`, expected `string`.'
|
||||
);
|
||||
@@ -1037,7 +1091,7 @@ describe('ReactCompositeComponent', function() {
|
||||
);
|
||||
|
||||
// Previous calls should not log errors
|
||||
expect(console.warn.mock.calls.length).toBe(2);
|
||||
expect(console.warn.mock.calls.length).toBe(4);
|
||||
});
|
||||
|
||||
it('should filter out context not in contextTypes', function() {
|
||||
@@ -1051,11 +1105,26 @@ describe('ReactCompositeComponent', function() {
|
||||
}
|
||||
});
|
||||
|
||||
var instance = React.withContext({foo: 'abc', bar: 123}, function() {
|
||||
return <Component />;
|
||||
var ComponentInFooBarContext = React.createClass({
|
||||
childContextTypes: {
|
||||
foo: ReactPropTypes.string,
|
||||
bar: ReactPropTypes.number
|
||||
},
|
||||
|
||||
getChildContext: function() {
|
||||
return {
|
||||
foo: 'abc',
|
||||
bar: 123
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <Component />;
|
||||
}
|
||||
});
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
reactComponentExpect(instance).scalarContextEqual({foo: 'abc'});
|
||||
|
||||
var instance = ReactTestUtils.renderIntoDocument(<ComponentInFooBarContext />);
|
||||
reactComponentExpect(instance).expectRenderedChild().scalarContextEqual({foo: 'abc'});
|
||||
});
|
||||
|
||||
it('should filter context properly in callbacks', function() {
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('ReactElement', function() {
|
||||
expect(element.props).toEqual({foo:'56'});
|
||||
});
|
||||
|
||||
it('preserves the context on the element', function() {
|
||||
it('preserves the legacy context on the element', function() {
|
||||
var Component = React.createFactory(ComponentClass);
|
||||
var element;
|
||||
|
||||
@@ -108,12 +108,6 @@ describe('ReactElement', function() {
|
||||
var element;
|
||||
|
||||
var Wrapper = React.createClass({
|
||||
childContextTypes: {
|
||||
foo: React.PropTypes.string
|
||||
},
|
||||
getChildContext: function() {
|
||||
return { foo: 'bar' };
|
||||
},
|
||||
render: function() {
|
||||
element = Component();
|
||||
return element;
|
||||
|
||||
@@ -328,23 +328,23 @@ assign(
|
||||
ReactCompositeComponent.ShallowMixin
|
||||
);
|
||||
|
||||
ReactShallowRenderer.prototype.render = function(element) {
|
||||
ReactShallowRenderer.prototype.render = function(element, context) {
|
||||
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
|
||||
this._render(element, transaction);
|
||||
this._render(element, transaction, context);
|
||||
ReactUpdates.ReactReconcileTransaction.release(transaction);
|
||||
};
|
||||
|
||||
ReactShallowRenderer.prototype._render = function(element, transaction) {
|
||||
ReactShallowRenderer.prototype._render = function(element, transaction, context) {
|
||||
if (!this._instance) {
|
||||
var rootID = ReactInstanceHandles.createReactRootID();
|
||||
var instance = new ShallowComponentWrapper(new element.type(element.props));
|
||||
instance.construct(element);
|
||||
|
||||
instance.mountComponent(rootID, transaction, 0);
|
||||
instance.mountComponent(rootID, transaction, 0, context);
|
||||
|
||||
this._instance = instance;
|
||||
} else {
|
||||
this._instance.receiveComponent(element, transaction);
|
||||
this._instance.receiveComponent(element, transaction, context);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('ReactTestUtils', function() {
|
||||
});
|
||||
|
||||
var shallowRenderer = ReactTestUtils.createRenderer();
|
||||
shallowRenderer.render(<SomeComponent />);
|
||||
shallowRenderer.render(<SomeComponent />, {});
|
||||
|
||||
var result = shallowRenderer.getRenderOutput();
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('ReactTestUtils', function() {
|
||||
});
|
||||
|
||||
var shallowRenderer = ReactTestUtils.createRenderer();
|
||||
shallowRenderer.render(<SomeComponent />);
|
||||
shallowRenderer.render(<SomeComponent />, {});
|
||||
var result = shallowRenderer.getRenderOutput();
|
||||
expect(result.type).toBe('div');
|
||||
expect(result.props.children).toEqual([
|
||||
@@ -99,7 +99,7 @@ describe('ReactTestUtils', function() {
|
||||
<span className="child2" />
|
||||
]);
|
||||
|
||||
shallowRenderer.render(<SomeComponent aNew="prop" />);
|
||||
shallowRenderer.render(<SomeComponent aNew="prop" />, {});
|
||||
var updatedResult = shallowRenderer.getRenderOutput();
|
||||
expect(updatedResult.type).toBe('a');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user