react-dom
Version:
React package for working with the DOM.
211 lines (176 loc) • 8.35 kB
JavaScript
/**
* Copyright 2013-present, 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.
*
*
*/
;
var _extends = _assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _prodInvariant = require('./reactProdInvariant'),
_assign = require('object-assign');
var emptyObject = require('fbjs/lib/emptyObject');
var invariant = require('fbjs/lib/invariant');
var _require = require('./ReactFiberTreeReflection'),
getComponentName = _require.getComponentName,
isFiberMounted = _require.isFiberMounted;
var _require2 = require('./ReactTypeOfWork'),
ClassComponent = _require2.ClassComponent,
HostRoot = _require2.HostRoot;
var _require3 = require('./ReactFiberStack'),
createCursor = _require3.createCursor,
pop = _require3.pop,
push = _require3.push;
if (process.env.NODE_ENV !== 'production') {
var checkReactTypeSpec = require('./checkReactTypeSpec');
}
// A cursor to the current merged context object on the stack.
var contextStackCursor = createCursor(emptyObject);
// A cursor to a boolean indicating whether the context has changed.
var didPerformWorkStackCursor = createCursor(false);
// Keep track of the previous context object that was on the stack.
// We use this to get access to the parent context after we have already
// pushed the next context provider, and now need to merge their contexts.
var previousContext = emptyObject;
function getUnmaskedContext(workInProgress) {
var hasOwnContext = isContextProvider(workInProgress);
if (hasOwnContext) {
// If the fiber is a context provider itself, when we read its context
// we have already pushed its own child context on the stack. A context
// provider should not "see" its own child context. Therefore we read the
// previous (parent) context instead for a context provider.
return previousContext;
}
return contextStackCursor.current;
}
exports.getUnmaskedContext = getUnmaskedContext;
function cacheContext(workInProgress, unmaskedContext, maskedContext) {
var instance = workInProgress.stateNode;
instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
}
exports.cacheContext = cacheContext;
exports.getMaskedContext = function (workInProgress, unmaskedContext) {
var type = workInProgress.type;
var contextTypes = type.contextTypes;
if (!contextTypes) {
return emptyObject;
}
// Avoid recreating masked context unless unmasked context has changed.
// Failing to do this will result in unnecessary calls to componentWillReceiveProps.
// This may trigger infinite loops if componentWillReceiveProps calls setState.
var instance = workInProgress.stateNode;
if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) {
return instance.__reactInternalMemoizedMaskedChildContext;
}
var context = {};
for (var key in contextTypes) {
context[key] = unmaskedContext[key];
}
if (process.env.NODE_ENV !== 'production') {
var name = getComponentName(workInProgress);
checkReactTypeSpec(contextTypes, context, 'context', name, null, workInProgress);
}
// Cache unmasked context so we can avoid recreating masked context unless necessary.
// Context is created before the class component is instantiated so check for instance.
if (instance) {
cacheContext(workInProgress, unmaskedContext, context);
}
return context;
};
exports.hasContextChanged = function () {
return didPerformWorkStackCursor.current;
};
function isContextConsumer(fiber) {
return fiber.tag === ClassComponent && fiber.type.contextTypes != null;
}
exports.isContextConsumer = isContextConsumer;
function isContextProvider(fiber) {
return fiber.tag === ClassComponent && fiber.type.childContextTypes != null;
}
exports.isContextProvider = isContextProvider;
function popContextProvider(fiber) {
if (!isContextProvider(fiber)) {
return;
}
pop(didPerformWorkStackCursor, fiber);
pop(contextStackCursor, fiber);
}
exports.popContextProvider = popContextProvider;
exports.pushTopLevelContextObject = function (fiber, context, didChange) {
invariant(contextStackCursor.cursor == null, 'Unexpected context found on stack');
push(contextStackCursor, context, fiber);
push(didPerformWorkStackCursor, didChange, fiber);
};
function processChildContext(fiber, parentContext, isReconciling) {
var instance = fiber.stateNode;
var childContextTypes = fiber.type.childContextTypes;
var childContext = instance.getChildContext();
for (var contextKey in childContext) {
!(contextKey in childContextTypes) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', getComponentName(fiber), contextKey) : _prodInvariant('108', getComponentName(fiber), contextKey) : void 0;
}
if (process.env.NODE_ENV !== 'production') {
var name = getComponentName(fiber);
// We can only provide accurate element stacks if we pass work-in-progress tree
// during the begin or complete phase. However currently this function is also
// called from unstable_renderSubtree legacy implementation. In this case it unsafe to
// assume anything about the given fiber. We won't pass it down if we aren't sure.
// TODO: remove this hack when we delete unstable_renderSubtree in Fiber.
var workInProgress = isReconciling ? fiber : null;
checkReactTypeSpec(childContextTypes, childContext, 'childContext', name, null, workInProgress);
}
return _extends({}, parentContext, childContext);
}
exports.processChildContext = processChildContext;
exports.pushContextProvider = function (workInProgress) {
if (!isContextProvider(workInProgress)) {
return false;
}
var instance = workInProgress.stateNode;
// We push the context as early as possible to ensure stack integrity.
// If the instance does not exist yet, we will push null at first,
// and replace it on the stack later when invalidating the context.
var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyObject;
// Remember the parent context so we can merge with it later.
previousContext = contextStackCursor.current;
push(contextStackCursor, memoizedMergedChildContext, workInProgress);
push(didPerformWorkStackCursor, false, workInProgress);
return true;
};
exports.invalidateContextProvider = function (workInProgress) {
var instance = workInProgress.stateNode;
invariant(instance, 'Expected to have an instance by this point.');
// Merge parent and own context.
var mergedContext = processChildContext(workInProgress, previousContext, true);
instance.__reactInternalMemoizedMergedChildContext = mergedContext;
// Replace the old (or empty) context with the new one.
// It is important to unwind the context in the reverse order.
pop(didPerformWorkStackCursor, workInProgress);
pop(contextStackCursor, workInProgress);
// Now push the new context and mark that it has changed.
push(contextStackCursor, mergedContext, workInProgress);
push(didPerformWorkStackCursor, true, workInProgress);
};
exports.resetContext = function () {
previousContext = emptyObject;
contextStackCursor.current = emptyObject;
didPerformWorkStackCursor.current = false;
};
exports.findCurrentUnmaskedContext = function (fiber) {
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
// makes sense elsewhere
invariant(isFiberMounted(fiber) && fiber.tag === ClassComponent, 'Expected subtree parent to be a mounted class component');
var node = fiber;
while (node.tag !== HostRoot) {
if (isContextProvider(node)) {
return node.stateNode.__reactInternalMemoizedMergedChildContext;
}
var parent = node['return'];
invariant(parent, 'Found unexpected detached subtree parent');
node = parent;
}
return node.stateNode.context;
};