react-dom
Version:
React package for working with the DOM.
288 lines (246 loc) • 13.7 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.
*
*
*/
'use strict';
var _prodInvariant = require('./reactProdInvariant');
var _require = require('./ReactFiberContext'),
cacheContext = _require.cacheContext,
getMaskedContext = _require.getMaskedContext,
getUnmaskedContext = _require.getUnmaskedContext,
isContextConsumer = _require.isContextConsumer;
var _require2 = require('./ReactFiberUpdateQueue'),
addUpdate = _require2.addUpdate,
addReplaceUpdate = _require2.addReplaceUpdate,
addForceUpdate = _require2.addForceUpdate,
beginUpdateQueue = _require2.beginUpdateQueue;
var _require3 = require('./ReactFiberContext'),
hasContextChanged = _require3.hasContextChanged;
var _require4 = require('./ReactFiberTreeReflection'),
getComponentName = _require4.getComponentName,
isMounted = _require4.isMounted;
var ReactInstanceMap = require('./ReactInstanceMap');
var emptyObject = require('fbjs/lib/emptyObject');
var shallowEqual = require('fbjs/lib/shallowEqual');
var warning = require('fbjs/lib/warning');
var invariant = require('fbjs/lib/invariant');
var isArray = Array.isArray;
module.exports = function (scheduleUpdate, getPriorityContext) {
// Class component state updater
var updater = {
isMounted: isMounted,
enqueueSetState: function (instance, partialState, callback) {
var fiber = ReactInstanceMap.get(instance);
var priorityLevel = getPriorityContext();
addUpdate(fiber, partialState, callback || null, priorityLevel);
scheduleUpdate(fiber, priorityLevel);
},
enqueueReplaceState: function (instance, state, callback) {
var fiber = ReactInstanceMap.get(instance);
var priorityLevel = getPriorityContext();
addReplaceUpdate(fiber, state, callback || null, priorityLevel);
scheduleUpdate(fiber, priorityLevel);
},
enqueueForceUpdate: function (instance, callback) {
var fiber = ReactInstanceMap.get(instance);
var priorityLevel = getPriorityContext();
addForceUpdate(fiber, callback || null, priorityLevel);
scheduleUpdate(fiber, priorityLevel);
}
};
function checkShouldComponentUpdate(workInProgress, oldProps, newProps, newState, newContext) {
if (oldProps === null || workInProgress.updateQueue && workInProgress.updateQueue.hasForceUpdate) {
// If the workInProgress already has an Update effect, return true
return true;
}
var instance = workInProgress.stateNode;
if (typeof instance.shouldComponentUpdate === 'function') {
var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, newContext);
if (process.env.NODE_ENV !== 'production') {
process.env.NODE_ENV !== 'production' ? warning(shouldUpdate !== undefined, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentName(workInProgress)) : void 0;
}
return shouldUpdate;
}
var type = workInProgress.type;
if (type.prototype && type.prototype.isPureReactComponent) {
return !shallowEqual(oldProps, newProps) || !shallowEqual(instance.state, newState);
}
return true;
}
function checkClassInstance(workInProgress) {
var instance = workInProgress.stateNode;
if (process.env.NODE_ENV !== 'production') {
var name = getComponentName(workInProgress);
var renderPresent = instance.render;
process.env.NODE_ENV !== 'production' ? warning(renderPresent, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', name) : void 0;
var noGetInitialStateOnES6 = !instance.getInitialState || instance.getInitialState.isReactClassApproved || instance.state;
process.env.NODE_ENV !== 'production' ? warning(noGetInitialStateOnES6, 'getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', name) : void 0;
var noGetDefaultPropsOnES6 = !instance.getDefaultProps || instance.getDefaultProps.isReactClassApproved;
process.env.NODE_ENV !== 'production' ? warning(noGetDefaultPropsOnES6, 'getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', name) : void 0;
var noInstancePropTypes = !instance.propTypes;
process.env.NODE_ENV !== 'production' ? warning(noInstancePropTypes, 'propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', name) : void 0;
var noInstanceContextTypes = !instance.contextTypes;
process.env.NODE_ENV !== 'production' ? warning(noInstanceContextTypes, 'contextTypes was defined as an instance property on %s. Use a static ' + 'property to define contextTypes instead.', name) : void 0;
var noComponentShouldUpdate = typeof instance.componentShouldUpdate !== 'function';
process.env.NODE_ENV !== 'production' ? warning(noComponentShouldUpdate, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', name) : void 0;
var noComponentDidUnmount = typeof instance.componentDidUnmount !== 'function';
process.env.NODE_ENV !== 'production' ? warning(noComponentDidUnmount, '%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', name) : void 0;
var noComponentWillRecieveProps = typeof instance.componentWillRecieveProps !== 'function';
process.env.NODE_ENV !== 'production' ? warning(noComponentWillRecieveProps, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name) : void 0;
var hasMutatedProps = instance.props !== workInProgress.pendingProps;
process.env.NODE_ENV !== 'production' ? warning(instance.props === undefined || !hasMutatedProps, '%s(...): When calling super() in `%s`, make sure to pass ' + 'up the same props that your component\'s constructor was passed.', name, name) : void 0;
}
var state = instance.state;
if (state && (typeof state !== 'object' || isArray(state))) {
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.state: must be set to an object or null', getComponentName(workInProgress)) : _prodInvariant('106', getComponentName(workInProgress)) : void 0;
}
if (typeof instance.getChildContext === 'function') {
!(typeof workInProgress.type.childContextTypes === 'object') ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().', getComponentName(workInProgress)) : _prodInvariant('107', getComponentName(workInProgress)) : void 0;
}
}
function adoptClassInstance(workInProgress, instance) {
instance.updater = updater;
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
ReactInstanceMap.set(instance, workInProgress);
}
function constructClassInstance(workInProgress) {
var ctor = workInProgress.type;
var props = workInProgress.pendingProps;
var unmaskedContext = getUnmaskedContext(workInProgress);
var needsContext = isContextConsumer(workInProgress);
var context = needsContext ? getMaskedContext(workInProgress, unmaskedContext) : emptyObject;
var instance = new ctor(props, context);
adoptClassInstance(workInProgress, instance);
checkClassInstance(workInProgress);
// Cache unmasked context so we can avoid recreating masked context unless necessary.
// ReactFiberContext usually updates this cache but can't for newly-created instances.
if (needsContext) {
cacheContext(workInProgress, unmaskedContext, context);
}
return instance;
}
// Invokes the mount life-cycles on a previously never rendered instance.
function mountClassInstance(workInProgress, priorityLevel) {
var instance = workInProgress.stateNode;
var state = instance.state || null;
var props = workInProgress.pendingProps;
if (!props) {
throw new Error('There must be pending props for an initial mount.');
}
var unmaskedContext = getUnmaskedContext(workInProgress);
instance.props = props;
instance.state = state;
instance.context = getMaskedContext(workInProgress, unmaskedContext);
if (typeof instance.componentWillMount === 'function') {
instance.componentWillMount();
// If we had additional state updates during this life-cycle, let's
// process them now.
var updateQueue = workInProgress.updateQueue;
if (updateQueue) {
instance.state = beginUpdateQueue(workInProgress, updateQueue, instance, state, props, priorityLevel);
}
}
}
// Called on a preexisting class instance. Returns false if a resumed render
// could be reused.
function resumeMountClassInstance(workInProgress, priorityLevel) {
var newState = workInProgress.memoizedState;
var newProps = workInProgress.pendingProps;
if (!newProps) {
// If there isn't any new props, then we'll reuse the memoized props.
// This could be from already completed work.
newProps = workInProgress.memoizedProps;
if (!newProps) {
throw new Error('There should always be pending or memoized props.');
}
}
var newUnmaskedContext = getUnmaskedContext(workInProgress);
var newContext = getMaskedContext(workInProgress, newUnmaskedContext);
// TODO: Should we deal with a setState that happened after the last
// componentWillMount and before this componentWillMount? Probably
// unsupported anyway.
if (!checkShouldComponentUpdate(workInProgress, workInProgress.memoizedProps, newProps, newState, newContext)) {
return false;
}
// If we didn't bail out we need to construct a new instance. We don't
// want to reuse one that failed to fully mount.
var newInstance = constructClassInstance(workInProgress);
newInstance.props = newProps;
newInstance.state = newState = newInstance.state || null;
newInstance.context = newContext;
if (typeof newInstance.componentWillMount === 'function') {
newInstance.componentWillMount();
}
// If we had additional state updates, process them now.
// They may be from componentWillMount() or from error boundary's setState()
// during initial mounting.
var newUpdateQueue = workInProgress.updateQueue;
if (newUpdateQueue) {
newInstance.state = beginUpdateQueue(workInProgress, newUpdateQueue, newInstance, newState, newProps, priorityLevel);
}
return true;
}
// Invokes the update life-cycles and returns false if it shouldn't rerender.
function updateClassInstance(current, workInProgress, priorityLevel) {
var instance = workInProgress.stateNode;
var oldProps = workInProgress.memoizedProps || current.memoizedProps;
var newProps = workInProgress.pendingProps;
if (!newProps) {
// If there aren't any new props, then we'll reuse the memoized props.
// This could be from already completed work.
newProps = oldProps;
if (!newProps) {
throw new Error('There should always be pending or memoized props.');
}
}
var oldContext = instance.context;
var newUnmaskedContext = getUnmaskedContext(workInProgress);
var newContext = getMaskedContext(workInProgress, newUnmaskedContext);
// Note: During these life-cycles, instance.props/instance.state are what
// ever the previously attempted to render - not the "current". However,
// during componentDidUpdate we pass the "current" props.
if (oldProps !== newProps || oldContext !== newContext) {
if (typeof instance.componentWillReceiveProps === 'function') {
instance.componentWillReceiveProps(newProps, newContext);
}
}
// Compute the next state using the memoized state and the update queue.
var updateQueue = workInProgress.updateQueue;
var oldState = workInProgress.memoizedState;
// TODO: Previous state can be null.
var newState = void 0;
if (updateQueue) {
newState = beginUpdateQueue(workInProgress, updateQueue, instance, oldState, newProps, priorityLevel);
} else {
newState = oldState;
}
if (oldProps === newProps && oldState === newState && !hasContextChanged() && !(updateQueue && updateQueue.hasForceUpdate)) {
return false;
}
if (!checkShouldComponentUpdate(workInProgress, oldProps, newProps, newState, newContext)) {
// TODO: Should this get the new props/state updated regardless?
return false;
}
if (typeof instance.componentWillUpdate === 'function') {
instance.componentWillUpdate(newProps, newState, newContext);
}
instance.props = newProps;
instance.state = newState;
instance.context = newContext;
return true;
}
return {
adoptClassInstance: adoptClassInstance,
constructClassInstance: constructClassInstance,
mountClassInstance: mountClassInstance,
resumeMountClassInstance: resumeMountClassInstance,
updateClassInstance: updateClassInstance
};
};