react——setState源码解析
源码在node_modules/react/.目录下
1、node_modules/react/dist/react.js
定义了ReactComponent
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; }
定义了ReactComponent的prototype
ReactComponent.prototype.isReactComponent = {}; ReactComponent.prototype.setState = function (partialState, callback) { !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? "development" !== 'production' ? invariant(false, 'setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.') : invariant(false) : undefined; if ("development" !== 'production') { "development" !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : undefined; } this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback); } }; ReactComponent.prototype.forceUpdate = function (callback) { this.updater.enqueueForceUpdate(this); if (callback) { this.updater.enqueueCallback(this, callback); } };
updater的两处定义在node_modules/react/lib/ReactUpdateQueue.js和node_modules/react/lib/ReactNoopUpdateQueue.js
2、node_modules/react/lib/ReactNoopUpdateQueue.js
定义了ReactNoopUpdateQueue的空操作Noop:
1 /** 2 * Copyright 2015, Facebook, Inc. 3 * All rights reserved. 4 * 5 * This source code is licensed under the BSD-style license found in the 6 * LICENSE file in the root directory of this source tree. An additional grant 7 * of patent rights can be found in the PATENTS file in the same directory. 8 * 9 * @providesModule ReactNoopUpdateQueue 10 */ 11 12 'use strict'; 13 14 var warning = require('fbjs/lib/warning'); 15 16 function warnTDZ(publicInstance, callerName) { 17 if (process.env.NODE_ENV !== 'production') { 18 process.env.NODE_ENV !== 'production' ? 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 || '') : undefined; 19 } 20 } 21 22 /** 23 * This is the abstract API for an update queue. 24 */ 25 var ReactNoopUpdateQueue = { 26 27 /** 28 * Checks whether or not this composite component is mounted. 29 * @param {ReactClass} publicInstance The instance we want to test. 30 * @return {boolean} True if mounted, false otherwise. 31 * @protected 32 * @final 33 */ 34 isMounted: function (publicInstance) { 35 return false; 36 }, 37 38 /** 39 * Enqueue a callback that will be executed after all the pending updates 40 * have processed. 41 * 42 * @param {ReactClass} publicInstance The instance to use as `this` context. 43 * @param {?function} callback Called after state is updated. 44 * @internal 45 */ 46 enqueueCallback: function (publicInstance, callback) {}, 47 48 /** 49 * Forces an update. This should only be invoked when it is known with 50 * certainty that we are **not** in a DOM transaction. 51 * 52 * You may want to call this when you know that some deeper aspect of the 53 * component's state has changed but `setState` was not called. 54 * 55 * This will not invoke `shouldComponentUpdate`, but it will invoke 56 * `componentWillUpdate` and `componentDidUpdate`. 57 * 58 * @param {ReactClass} publicInstance The instance that should rerender. 59 * @internal 60 */ 61 enqueueForceUpdate: function (publicInstance) { 62 warnTDZ(publicInstance, 'forceUpdate'); 63 }, 64 65 /** 66 * Replaces all of the state. Always use this or `setState` to mutate state. 67 * You should treat `this.state` as immutable. 68 * 69 * There is no guarantee that `this.state` will be immediately updated, so 70 * accessing `this.state` after calling this method may return the old value. 71 * 72 * @param {ReactClass} publicInstance The instance that should rerender. 73 * @param {object} completeState Next state. 74 * @internal 75 */ 76 enqueueReplaceState: function (publicInstance, completeState) { 77 warnTDZ(publicInstance, 'replaceState'); 78 }, 79 80 /** 81 * Sets a subset of the state. This only exists because _pendingState is 82 * internal. This provides a merging strategy that is not available to deep 83 * properties which is confusing. TODO: Expose pendingState or don't use it 84 * during the merge. 85 * 86 * @param {ReactClass} publicInstance The instance that should rerender. 87 * @param {object} partialState Next partial state to be merged with state. 88 * @internal 89 */ 90 enqueueSetState: function (publicInstance, partialState) { 91 warnTDZ(publicInstance, 'setState'); 92 }, 93 94 /** 95 * Sets a subset of the props. 96 * 97 * @param {ReactClass} publicInstance The instance that should rerender. 98 * @param {object} partialProps Subset of the next props. 99 * @internal 100 */ 101 enqueueSetProps: function (publicInstance, partialProps) { 102 warnTDZ(publicInstance, 'setProps'); 103 }, 104 105 /** 106 * Replaces all of the props. 107 * 108 * @param {ReactClass} publicInstance The instance that should rerender. 109 * @param {object} props New props. 110 * @internal 111 */ 112 enqueueReplaceProps: function (publicInstance, props) { 113 warnTDZ(publicInstance, 'replaceProps'); 114 } 115 116 }; 117 118 module.exports = ReactNoopUpdateQueue;
主要看ReactUpdateQueue.js
3、node_modules/react/lib/ReactUpdateQueue.js
这儿主要分析enqueueCallback和enqueueSetState。
var ReactUpdateQueue = { /** * 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) { !(typeof callback === 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' + '`setState`, `replaceState`, or `forceUpdate` with a callback that ' + 'isn\'t callable.') : invariant(false) : undefined; var internalInstance = getInternalInstanceReadyForUpdate(publicInstance); // Previously we would throw an error if we didn't have an internal // instance. Since we want to make it a no-op instead, we mirror the same // behavior we have in other enqueue* methods. // We also need to ignore callbacks in componentWillMount. See // enqueueUpdates. if (!internalInstance) { return null; } if (internalInstance._pendingCallbacks) { internalInstance._pendingCallbacks.push(callback); } else { internalInstance._pendingCallbacks = [callback]; } // TODO: The callback here is ignored when setState is called from // componentWillMount. Either fix it or disallow doing so completely in // favor of getInitialState. Alternatively, we can disallow // componentWillMount during server-side rendering. enqueueUpdate(internalInstance); },
}