UNPKG

react-dom

Version:

React package for working with the DOM.

288 lines (246 loc) • 13.7 kB
/** * 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 }; };