IndeterminateComponent
1 )概述
- 这是一个比较特殊的component的类型, 就是还没有被指定类型的component
- 在一个fibrer被创建的时候,它的tag可能会是
IndeterminateComponent
- 在 packages/react-reconciler/src/ReactFiber.js 中,有一个方法
-
createFiberFromTypeAndProps
中,一开始就声明了let fiberTag = IndeterminateComponent; let resolvedType = type; // 一开始 if (typeof type === 'function') { // 在 function 下只是判断了 constructor是否存在 // 在不存在的时候,是否认为它是一个 FunctionComponent 呢,实际上并没有 // 实际上我们写的任何一个 function component 一开始就是 IndeterminateComponent 类型 if (shouldConstruct(type)) { // 存在,则赋值为 ClassComponent fiberTag = ClassComponent; } } else if (typeof type === 'string') { fiberTag = HostComponent; } else { // 省略 }
- 在最终调用
createFiber
创建 Fiber 对象
2 )源码文章来源:https://www.toymoban.com/news/detail-811797.html
定位到 packages/react-reconciler/src/ReactFiber.js
进入 mountIndeterminateComponent 方法文章来源地址https://www.toymoban.com/news/detail-811797.html
//
function mountIndeterminateComponent(
_current,
workInProgress,
Component,
renderExpirationTime,
) {
// 首先判断 _current 是否存在,存在则进行初始化操作
// 因为只有在第一次渲染的时候,才有 indeterminate component 这种情况
// 经过第一次渲染之后,我们就会发现 indeterminate component 的具体的类型
// 这种情况,可能是中途抛出一个 error 或 promise 等情况,比如 Suspense 组件
// 这时候初始化是为了 去除 _current 和 workInProgress 相关联系,因为需要重新进行初次渲染的流程
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); // 调用 Component 方法
}
// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
// 符合这个条件,认为是 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; // 这里认为它是一个 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;
}
}
- 基于上述判断条件 能认定是一个 ClassComponent,后续渲染一定会按照 ClassComponent 进行
if ( typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined ){}
- 现在来测试一下,看下 function component 是否可以执行
import React from 'react' export default function TestIndeterminationComponent() { return { componentDidMount() { console.log('invoker') }, render() { return <span>aaa</span> } } }
- 上述都能正常显示,以及打印 console 输出
- 也就是说,对于一个 function component, 如果里面 return 的对象,具有 render 方法
- 就认为它是一个 class component 一样的类型,去使用它
- 并且在里面声明的生命周期方法都会被它调用
- 这是
IndeterminateComponent
的特性
- 在最初我们渲染的时候,所有的 function component 都是
IndeterminateComponent
的类型 - 在第一次渲染之后,我们根据渲染类型的返回,最终得到具体类型
- 可以通过 function component 返回一个对象的方式去渲染一个类似 classComponent 这样的类型
- 注意,一般不推荐这么写
到了这里,关于React16源码: React中的IndeterminateComponent的源码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!