IndeterminateComponent
1 )概述
- 這是一個(gè)比較特殊的component的類型, 就是還沒有被指定類型的component
- 在一個(gè)fibrer被創(chuàng)建的時(shí)候,它的tag可能會(huì)是
IndeterminateComponent
- 在 packages/react-reconciler/src/ReactFiber.js 中,有一個(gè)方法
-
createFiberFromTypeAndProps
中,一開始就聲明了let fiberTag = IndeterminateComponent; let resolvedType = type; // 一開始 if (typeof type === 'function') { // 在 function 下只是判斷了 constructor是否存在 // 在不存在的時(shí)候,是否認(rèn)為它是一個(gè) FunctionComponent 呢,實(shí)際上并沒有 // 實(shí)際上我們寫的任何一個(gè) function component 一開始就是 IndeterminateComponent 類型 if (shouldConstruct(type)) { // 存在,則賦值為 ClassComponent fiberTag = ClassComponent; } } else if (typeof type === 'string') { fiberTag = HostComponent; } else { // 省略 }
- 在最終調(diào)用
createFiber
創(chuàng)建 Fiber 對(duì)象
2 )源碼文章來源:http://www.zghlxwxcb.cn/news/detail-811797.html
定位到 packages/react-reconciler/src/ReactFiber.js
進(jìn)入 mountIndeterminateComponent 方法文章來源地址http://www.zghlxwxcb.cn/news/detail-811797.html
//
function mountIndeterminateComponent(
_current,
workInProgress,
Component,
renderExpirationTime,
) {
// 首先判斷 _current 是否存在,存在則進(jìn)行初始化操作
// 因?yàn)橹挥性诘谝淮武秩镜臅r(shí)候,才有 indeterminate component 這種情況
// 經(jīng)過第一次渲染之后,我們就會(huì)發(fā)現(xiàn) indeterminate component 的具體的類型
// 這種情況,可能是中途拋出一個(gè) error 或 promise 等情況,比如 Suspense 組件
// 這時(shí)候初始化是為了 去除 _current 和 workInProgress 相關(guān)聯(lián)系,因?yàn)樾枰匦逻M(jìn)行初次渲染的流程
if (_current !== null) {
// An indeterminate component only mounts if it suspended inside a non-
// concurrent tree, in an inconsistent state. We want to treat it like
// a new mount, even though an empty version of it already committed.
// Disconnect the alternate pointers.
_current.alternate = null;
workInProgress.alternate = null;
// Since this is conceptually a new fiber, schedule a Placement effect
workInProgress.effectTag |= Placement;
}
const props = workInProgress.pendingProps;
const unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
const context = getMaskedContext(workInProgress, unmaskedContext);
prepareToReadContext(workInProgress, renderExpirationTime);
prepareToUseHooks(null, workInProgress, renderExpirationTime);
let value;
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;
}
}
if (workInProgress.mode & StrictMode) {
ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
}
ReactCurrentOwner.current = workInProgress;
value = Component(props, context);
} else {
value = Component(props, context); // 調(diào)用 Component 方法
}
// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
// 符合這個(gè)條件,認(rèn)為是 ClassComponent, 具有 render 方法
if (
typeof value === 'object' &&
value !== null &&
typeof value.render === 'function' &&
value.$$typeof === undefined
) {
// Proceed under the assumption that this is a class instance
workInProgress.tag = ClassComponent; // 這里認(rèn)為它是一個(gè) ClassComponent
// Throw out any hooks that were used.
resetHooks();
// Push context providers early to prevent context stack mismatches.
// During mounting we don't know the child context yet as the instance doesn't exist.
// We will invalidate the child context in finishClassComponent() right after rendering.
let hasContext = false;
if (isLegacyContextProvider(Component)) {
hasContext = true;
pushLegacyContextProvider(workInProgress);
} else {
hasContext = false;
}
workInProgress.memoizedState =
value.state !== null && value.state !== undefined ? value.state : null;
const getDerivedStateFromProps = Component.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(
workInProgress,
Component,
getDerivedStateFromProps,
props,
);
}
adoptClassInstance(workInProgress, value);
mountClassInstance(workInProgress, Component, props, renderExpirationTime);
return finishClassComponent(
null,
workInProgress,
Component,
true,
hasContext,
renderExpirationTime,
);
} else {
// 否則按照 FunctionComponent 來渲染
// Proceed under the assumption that this is a function component
workInProgress.tag = FunctionComponent;
value = finishHooks(Component, props, value, context);
if (__DEV__) {
if (Component) {
warningWithoutStack(
!Component.childContextTypes,
'%s(...): childContextTypes cannot be defined on a function component.',
Component.displayName || Component.name || 'Component',
);
}
if (workInProgress.ref !== null) {
let info = '';
const ownerName = ReactCurrentFiber.getCurrentFiberOwnerNameInDevOrNull();
if (ownerName) {
info += '\n\nCheck the render method of `' + ownerName + '`.';
}
let warningKey = ownerName || workInProgress._debugID || '';
const debugSource = workInProgress._debugSource;
if (debugSource) {
warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
}
if (!didWarnAboutFunctionRefs[warningKey]) {
didWarnAboutFunctionRefs[warningKey] = true;
warning(
false,
'Function components cannot be given refs. ' +
'Attempts to access this ref will fail.%s',
info,
);
}
}
if (typeof Component.getDerivedStateFromProps === 'function') {
const componentName = getComponentName(Component) || 'Unknown';
if (!didWarnAboutGetDerivedStateOnFunctionComponent[componentName]) {
warningWithoutStack(
false,
'%s: Function components do not support getDerivedStateFromProps.',
componentName,
);
didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = true;
}
}
if (
typeof Component.contextType === 'object' &&
Component.contextType !== null
) {
const componentName = getComponentName(Component) || 'Unknown';
if (!didWarnAboutContextTypeOnFunctionComponent[componentName]) {
warningWithoutStack(
false,
'%s: Function components do not support contextType.',
componentName,
);
didWarnAboutContextTypeOnFunctionComponent[componentName] = true;
}
}
}
reconcileChildren(null, workInProgress, value, renderExpirationTime);
return workInProgress.child;
}
}
- 基于上述判斷條件 能認(rèn)定是一個(gè) ClassComponent,后續(xù)渲染一定會(huì)按照 ClassComponent 進(jìn)行
if ( typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined ){}
- 現(xiàn)在來測(cè)試一下,看下 function component 是否可以執(zhí)行
import React from 'react' export default function TestIndeterminationComponent() { return { componentDidMount() { console.log('invoker') }, render() { return <span>aaa</span> } } }
- 上述都能正常顯示,以及打印 console 輸出
- 也就是說,對(duì)于一個(gè) function component, 如果里面 return 的對(duì)象,具有 render 方法
- 就認(rèn)為它是一個(gè) class component 一樣的類型,去使用它
- 并且在里面聲明的生命周期方法都會(huì)被它調(diào)用
- 這是
IndeterminateComponent
的特性
- 在最初我們渲染的時(shí)候,所有的 function component 都是
IndeterminateComponent
的類型 - 在第一次渲染之后,我們根據(jù)渲染類型的返回,最終得到具體類型
- 可以通過 function component 返回一個(gè)對(duì)象的方式去渲染一個(gè)類似 classComponent 這樣的類型
- 注意,一般不推薦這么寫
到了這里,關(guān)于React16源碼: React中的IndeterminateComponent的源碼實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!