通過API:typeOf理解react中各種元件型別的關係。理解portal型別與reactElement型別的構建過程,與使用區別。梳理react中的型別,兩大型別的構建過程。
API
typeOf
isConcurrentMode 併發模式
isContextConsumer
isContextProvider
isElement
isForwardRef
isFragment
isProfiler 效能分析模式
isPortal
isStrictMode 嚴格模式
複製程式碼
memo元件的構建過程
function testMemo(){
const MyComponent = React.memo(function MyComponent(props) {
return (
<div>
this is memo
</div>
)
});
console.log("<MyComponent />",<MyComponent />)
console.log("MyComponent",MyComponent)
ReactDOM.render(
<MyComponent />,
document.getElementById('app')
);
}
複製程式碼
構建memo型別物件
React.memo函式返回一個react型別物件
{
$$typeof: REACT_MEMO_TYPE,
type,
compare: compare === undefined ? null : compare,
}
複製程式碼
列印結果為:
$$typeof:Symbol(react.memo)
compare:null
type:ƒ MyComponent(props)
複製程式碼
將memo型別物件構建為reactElement物件
執行語句的時候,就是呼叫createElement函式將這個物件建立為一個reactElement:
$$typeof:Symbol(react.element)
key:null
props:{}
ref:null
type:{$$typeof: Symbol(react.memo), type: ƒ, compare: null}
_owner:null
_store:{validated: false}
複製程式碼
createPortal的構建過程
const app = document.getElementById('app');
const Portal = ReactDOM.createPortal(<div>"portal"</div>, app);
console.log("Portal",Portal)
ReactDOM.render(
Portal,
document.getElementById('app')
);
複製程式碼
上述程式碼,react先將<div>"portal"</div>
部分呼叫createElement生成ReactElement物件,然後作為引數children傳入,createPortal接收兩個引數:children和真實的DOM或者元件。然後返回一個portal型別的react物件,與reactElement型別區別開,該portal不能再被建立為reactElement型別,即不能寫成。因為建立為reactElement型別物件,會呼叫createElementWithValidation對Portal進行驗證,呼叫isValidElementType(Portal)返回的結果為false,直接報錯。具體細節請參看9、react原始碼淺析(三):ReactElementValidator
isValidElementType原始碼為:
export default function isValidElementType(type: mixed) {
return (
typeof type === 'string' ||
typeof type === 'function' ||
// Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
type === REACT_FRAGMENT_TYPE ||
type === REACT_CONCURRENT_MODE_TYPE ||
type === REACT_PROFILER_TYPE ||
type === REACT_STRICT_MODE_TYPE ||
type === REACT_SUSPENSE_TYPE ||
(typeof type === 'object' &&
type !== null &&
(type.$$typeof === REACT_LAZY_TYPE ||
type.$$typeof === REACT_MEMO_TYPE ||
type.$$typeof === REACT_PROVIDER_TYPE ||
type.$$typeof === REACT_CONTEXT_TYPE ||
type.$$typeof === REACT_FORWARD_REF_TYPE))
);
}
複製程式碼
報錯為:
warning(
false,
'React.createElement: type is invalid -- expected a string (for ' +
'built-in components) or a class/function (for composite ' +
'components) but got: %s.%s',
typeString,
info,
);
複製程式碼
typeOf
用於判斷傳入的react的型別,一定是經過ReactElement react元素型別為兩大類:
reactType1 : {
$$typeof : REACT_ELEMENT_TYPE,
type : REACT_ASYNC_MODE_TYPE |
REACT_CONCURRENT_MODE_TYPE |
REACT_FRAGMENT_TYPE |
REACT_PROFILER_TYPE |
REACT_STRICT_MODE_TYPE |
REACT_SUSPENSE_TYPE | //返回REACT_CONTEXT_TYPE,REACT_FORWARD_REF_TYPE,REACT_PROVIDER_TYPE,REACT_ELEMENT_TYPE中的一種
{
$$typeof : REACT_CONTEXT_TYPE |
REACT_FORWARD_REF_TYPE |
REACT_PROVIDER_TYPE |
REACT_MEMO_TYPE | //typeOf返回REACT_ELEMENT_TYPE
REACT_LAZY_TYPE //typeOf返回REACT_ELEMENT_TYPE
}
}
reactType2 : {
$$typeof : REACT_PORTAL_TYPE
}
複製程式碼
注意:
- 其中type.$$typeof還可以為REACT_MEMO_TYPE或者REACT_LAZY_TYPE,typeOf在檢測到是REACT_MEMO_TYPE或者REACT_LAZY_TYPE的時候,返回REACT_ELEMENT_TYPE。
- type還可以是REACT_SUSPENSE_TYPE,但是typeOf在檢測到REACT_SUSPENSE_TYPE的時候,會繼續檢測type.$$typeof的值,可以返回REACT_CONTEXT_TYPE,REACT_FORWARD_REF_TYPE,REACT_PROVIDER_TYPE,REACT_ELEMENT_TYPE中的一種。
原始碼:
export function typeOf(object: any) {
if (typeof object === 'object' && object !== null) {
const $$typeof = object.$$typeof;
//先判斷object.$$typeof,
// 如果是REACT_ELEMENT_TYPE,繼續判斷type
// 如果是REACT_PORTAL_TYPE,返回REACT_PORTAL_TYPE
switch ($$typeof) {
case REACT_ELEMENT_TYPE:
const type = object.type;
//判斷object.type,如果是如下型別則返回該型別:
// REACT_ASYNC_MODE_TYPE
// REACT_CONCURRENT_MODE_TYPE
// REACT_FRAGMENT_TYPE
// REACT_PROFILER_TYPE
// REACT_STRICT_MODE_TYPE
// 如果都不是,則判斷object.type.$$typeof
switch (type) {
case REACT_ASYNC_MODE_TYPE:
case REACT_CONCURRENT_MODE_TYPE:
case REACT_FRAGMENT_TYPE:
case REACT_PROFILER_TYPE:
case REACT_STRICT_MODE_TYPE:
return type;
default:
const $$typeofType = type && type.$$typeof;
// 如果object.type.$$typeof是如下型別,則返回該型別:
// REACT_CONTEXT_TYPE
// REACT_FORWARD_REF_TYPE
// REACT_PROVIDER_TYPE
// 如果都不是則返回object.$$typeof,該值不屬於react型別
switch ($$typeofType) {
case REACT_CONTEXT_TYPE:
case REACT_FORWARD_REF_TYPE:
case REACT_PROVIDER_TYPE:
return $$typeofType;
default:
//檢測到是REACT_MEMO_TYPE或者REACT_LAZY_TYPE的時候,返回REACT_ELEMENT_TYPE
return $$typeof;
}
}
case REACT_PORTAL_TYPE:
return $$typeof;
}
}
複製程式碼