react原始碼淺析(四):react-is

hy醬發表於2019-03-02

react原始碼淺析(四):react-is

react相關庫原始碼淺析react ts3 專案

通過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
}
複製程式碼

注意:

  1. 其中type.$$typeof還可以為REACT_MEMO_TYPE或者REACT_LAZY_TYPE,typeOf在檢測到是REACT_MEMO_TYPE或者REACT_LAZY_TYPE的時候,返回REACT_ELEMENT_TYPE。
  2. 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;
    }
  }
複製程式碼

相關文章