react的setState批量更新与vue有很大差别,vue是利用异步函数batch执行所有更改的state,而react是抽取setState统一执行。
源码流程:
packages/react/src/reactBaseClass.js
/**
* Sets a subset of the state. Always use this to mutate
* state. You should treat `this.state` as immutable.
*
* There is no guarantee that `this.state` will be immediately updated, so
* accessing `this.state` after calling this method may return the old value.
*
* There is no guarantee that calls to `setState` will run synchronously,
* as they may eventually be batched together. You can provide an optional
* callback that will be executed when the call to setState is actually
* completed.
*
* When a function is provided to setState, it will be called at some point in
* the future (not synchronously). It will be called with the up to date
* component arguments (state, props, context). These values can be different
* from this.* because your function may be called after receiveProps but before
* shouldComponentUpdate, and this new state, props, and context will not yet be
* assigned to this.
*
* @param {object|function} partialState Next partial state or function to
* produce next partial state to be merged with current state.
* @param {?function} callback Called after state is updated.
* @final
* @protected
*/
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
被注入的updater 在ssr shallow client状态下各不相同,
因为client之下16版本以后的fiber极其复杂,就不上代码了
ssr代码片段简单看一下抽取流程
ReactParticalRerender.js
function resolve(
child: mixed,
context: Object,
threadID: ThreadID,
): {|
child: mixed,
context: Object,
|} {
while (React.isValidElement(child)) {
// Safe because we just checked it's an element.
let element: ReactElement = (child: any);
let Component = element.type;
if (__DEV__) {
pushElementToDebugStack(element);
}
if (typeof Component !== 'function') {
break;
}
processChild(element, Component);
}
// Extra closure so queue and replace can be captured properly
function processChild(element, Component) {
let publicContext = processContext(Component, context, threadID);
let queue = [];
let replace = false;
let updater = {
isMounted: function(publicInstance) {
return false;
},
enqueueForceUpdate: function(publicInstance) {
if (queue === null) {
warnNoop(publicInstance, 'forceUpdate');
return null;
}
},
enqueueReplaceState: function(publicInstance, completeState) {
replace = true;
queue = [completeState];
},
enqueueSetState: function(publicInstance, currentPartialState) {
if (queue === null) {
warnNoop(publicInstance, 'setState');
return null;
}
queue.push(currentPartialState);
},
};
let inst;
if (shouldConstruct(Component)) {
inst = new Component(element.props, publicContext, updater);
if (typeof Component.getDerivedStateFromProps === 'function') {
if (__DEV__) {
if (inst.state === null || inst.state === undefined) {
const componentName = getComponentName(Component) || 'Unknown';
if (!didWarnAboutUninitializedState[componentName]) {
warningWithoutStack(
false,
'`%s` uses `getDerivedStateFromProps` but its initial state is ' +
'%s. This is not recommended. Instead, define the initial state by ' +
'assigning an object to `this.state` in the constructor of `%s`. ' +
'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.',
componentName,
inst.state === null ? 'null' : 'undefined',
componentName,
);
didWarnAboutUninitializedState[componentName] = true;
}
}
}
let partialState = Component.getDerivedStateFromProps.call(
null,
element.props,
inst.state,
);
if (__DEV__) {
if (partialState === undefined) {
const componentName = getComponentName(Component) || 'Unknown';
if (!didWarnAboutUndefinedDerivedState[componentName]) {
warningWithoutStack(
false,
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
'You have returned undefined.',
componentName,
);
didWarnAboutUndefinedDerivedState[componentName] = true;
}
}
}
if (partialState != null) {
inst.state = Object.assign({}, inst.state, partialState);
}
}
} else {
if (__DEV__) {
if (
Component.prototype &&
typeof Component.prototype.render === 'function'
) {
const componentName = getComponentName(Component) || 'Unknown';
if (!didWarnAboutBadClass[componentName]) {
warningWithoutStack(
false,
"The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
'This is likely to cause errors. Change %s to extend React.Component instead.',
componentName,
componentName,
);
didWarnAboutBadClass[componentName] = true;
}
}
}
const componentIdentity = {};
prepareToUseHooks(componentIdentity);
inst = Component(element.props, publicContext, updater);
inst = finishHooks(Component, element.props, inst, publicContext);
if (inst == null || inst.render == null) {
child = inst;
validateRenderResult(child, Component);
return;
}
}
inst.props = element.props;
inst.context = publicContext;
inst.updater = updater;
let initialState = inst.state;
if (initialState === undefined) {
inst.state = initialState = null;
}
if (
typeof inst.UNSAFE_componentWillMount === 'function' ||
typeof inst.componentWillMount === 'function'
) {
if (typeof inst.componentWillMount === 'function') {
if (__DEV__) {
if (
warnAboutDeprecatedLifecycles &&
inst.componentWillMount.__suppressDeprecationWarning !== true
) {
const componentName = getComponentName(Component) || 'Unknown';
if (!didWarnAboutDeprecatedWillMount[componentName]) {
lowPriorityWarning(
false,
'%s: componentWillMount() is deprecated and will be ' +
'removed in the next major version. Read about the motivations ' +
'behind this change: ' +
'https://fb.me/react-async-component-lifecycle-hooks' +
'\n\n' +
'As a temporary workaround, you can rename to ' +
'UNSAFE_componentWillMount instead.',
componentName,
);
didWarnAboutDeprecatedWillMount[componentName] = true;
}
}
}
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for any component with the new gDSFP.
if (typeof Component.getDerivedStateFromProps !== 'function') {
inst.componentWillMount();
}
}
if (
typeof inst.UNSAFE_componentWillMount === 'function' &&
typeof Component.getDerivedStateFromProps !== 'function'
) {
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for any component with the new gDSFP.
inst.UNSAFE_componentWillMount();
}
if (queue.length) {
let oldQueue = queue;
let oldReplace = replace;
queue = null;
replace = false;
if (oldReplace && oldQueue.length === 1) {
inst.state = oldQueue[0];
} else {
let nextState = oldReplace ? oldQueue[0] : inst.state;
let dontMutate = true;
for (let i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
let partial = oldQueue[i];
let partialState =
typeof partial === 'function'
? partial.call(inst, nextState, element.props, publicContext)
: partial;
if (partialState != null) {
if (dontMutate) {
dontMutate = false;
nextState = Object.assign({}, nextState, partialState);
} else {
Object.assign(nextState, partialState);
}
}
}
inst.state = nextState;
}
} else {
queue = null;
}
}
child = inst.render();
if (__DEV__) {
if (child === undefined && inst.render._isMockFunction) {
// This is probably bad practice. Consider warning here and
// deprecating this convenience.
child = null;
}
}
validateRenderResult(child, Component);
let childContext;
if (typeof inst.getChildContext === 'function') {
let childContextTypes = Component.childContextTypes;
if (typeof childContextTypes === 'object') {
childContext = inst.getChildContext();
for (let contextKey in childContext) {
invariant(
contextKey in childContextTypes,
'%s.getChildContext(): key "%s" is not defined in childContextTypes.',
getComponentName(Component) || 'Unknown',
contextKey,
);
}
} else {
warningWithoutStack(
false,
'%s.getChildContext(): childContextTypes must be defined in order to ' +
'use getChildContext().',
getComponentName(Component) || 'Unknown',
);
}
}
if (childContext) {
context = Object.assign({}, context, childContext);
}
}
return {child, context};
}
网友评论