1. 程式人生 > 其它 >React 類元件的一些基本概念

React 類元件的一些基本概念

談談 React 裡面的一些型別及使用場景

  • React.Component<P,S,SS>, 這個型別絕對是 react 裡面的一哥,P 是 props, S: State, SS: SnapShot. 凡是 class 元件,都得繼承這個基類。下面是這個類的 type.d.ts. 生命週期主要參考下面這副圖 類生命週期 .
    這幅圖是新舊生命週期執行的圖,橘色是 deprecated, 綠色是新的,新老不能同時定義,否則會報錯。
interface Component<P = {}, S = {}, SS = any>
  extends ComponentLifecycle<P, S, SS> {}

ComponentLifecycle

interface ComponentLifecycle<P, S, SS = any>
  extends NewLifecycle<P, S, SS>,
    DeprecatedLifecycle<P, S> {
  /**
   * Called immediately after a component is mounted. Setting state here will trigger re-rendering.
   */
  componentDidMount?(): void;
  /**
   * Called to determine whether the change in props and state should trigger a re-render.
   *
   * `Component` always returns true.
   * `PureComponent` implements a shallow comparison on props and state and returns true if any
   * props or states have changed.
   *
   * If false is returned, `Component#render`, `componentWillUpdate`
   * and `componentDidUpdate` will not be called.
   */
  shouldComponentUpdate?(
    nextProps: Readonly<P>,
    nextState: Readonly<S>,
    nextContext: any
  ): boolean;
  /**
   * Called immediately before a component is destroyed. Perform any necessary cleanup in this method, such as
   * cancelled network requests, or cleaning up any DOM elements created in `componentDidMount`.
   */
  componentWillUnmount?(): void;
  /**
   * Catches exceptions generated in descendant components. Unhandled exceptions will cause
   * the entire component tree to unmount.
   */
  componentDidCatch?(error: Error, errorInfo: ErrorInfo): void;
}

NewLifecycle

interface NewLifecycle<P, S, SS> {
  /**
   * Runs before React applies the result of `render` to the document, and
   * returns an object to be given to componentDidUpdate. Useful for saving
   * things such as scroll position before `render` causes changes to it.
   *
   * Note: the presence of getSnapshotBeforeUpdate prevents any of the deprecated
   * lifecycle events from running.
   */
  getSnapshotBeforeUpdate?(
    prevProps: Readonly<P>,
    prevState: Readonly<S>
  ): SS | null;
  /**
   * Called immediately after updating occurs. Not called for the initial render.
   *
   * The snapshot is only present if getSnapshotBeforeUpdate is present and returns non-null.
   */
  componentDidUpdate?(
    prevProps: Readonly<P>,
    prevState: Readonly<S>,
    snapshot?: SS
  ): void;
}

DeprecatedLifecycle

interface DeprecatedLifecycle<P, S> {
  /**
   * Called immediately before mounting occurs, and before `Component#render`.
   * Avoid introducing any side-effects or subscriptions in this method.
   *
   * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
   * prevents this from being invoked.
   *
   * @deprecated 16.3, use componentDidMount or the constructor instead; will stop working in React 17
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
   */
  componentWillMount?(): void;
  /**
   * Called immediately before mounting occurs, and before `Component#render`.
   * Avoid introducing any side-effects or subscriptions in this method.
   *
   * This method will not stop working in React 17.
   *
   * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
   * prevents this from being invoked.
   *
   * @deprecated 16.3, use componentDidMount or the constructor instead
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
   */
  UNSAFE_componentWillMount?(): void;
  /**
   * Called when the component may be receiving new props.
   * React may call this even if props have not changed, so be sure to compare new and existing
   * props if you only want to handle changes.
   *
   * Calling `Component#setState` generally does not trigger this method.
   *
   * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
   * prevents this from being invoked.
   *
   * @deprecated 16.3, use static getDerivedStateFromProps instead; will stop working in React 17
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
   */
  componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
  /**
   * Called when the component may be receiving new props.
   * React may call this even if props have not changed, so be sure to compare new and existing
   * props if you only want to handle changes.
   *
   * Calling `Component#setState` generally does not trigger this method.
   *
   * This method will not stop working in React 17.
   *
   * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
   * prevents this from being invoked.
   *
   * @deprecated 16.3, use static getDerivedStateFromProps instead
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
   */
  UNSAFE_componentWillReceiveProps?(
    nextProps: Readonly<P>,
    nextContext: any
  ): void;
  /**
   * Called immediately before rendering when new props or state is received. Not called for the initial render.
   *
   * Note: You cannot call `Component#setState` here.
   *
   * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
   * prevents this from being invoked.
   *
   * @deprecated 16.3, use getSnapshotBeforeUpdate instead; will stop working in React 17
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
   */
  componentWillUpdate?(
    nextProps: Readonly<P>,
    nextState: Readonly<S>,
    nextContext: any
  ): void;
  /**
   * Called immediately before rendering when new props or state is received. Not called for the initial render.
   *
   * Note: You cannot call `Component#setState` here.
   *
   * This method will not stop working in React 17.
   *
   * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
   * prevents this from being invoked.
   *
   * @deprecated 16.3, use getSnapshotBeforeUpdate instead
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update
   * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
   */
  UNSAFE_componentWillUpdate?(
    nextProps: Readonly<P>,
    nextState: Readonly<S>,
    nextContext: any
  ): void;
}

  • React.FC

    函式元件,這個是絕對的後起之秀,react 的希望,官方推薦使用定義元件的方案。 它的相關型別比較多。例如 無狀態元件,函式元件等等,它的執行過程就相對簡單多了,就是函式的執行,從上往下執行,注意幾個底層的 hook 函式

    • const [val,setVal]=React.useState<S>(initialState), 它的特點是函式第一次執行的時候,或者val !==undefine 的時候才會執行,第二次以後就不會執行了,其實是內部直接返回了. 注意,它必須至於 function 內部的最頂層,不能存在於任何塊作用域。否則會報錯。
    • const context = React.useContext(Context), 跟上面的類似,也是第一次執行,以後只有 Context.provider 的值變化的時候才會執行。
    • const ref = React.useRef<r>(), 這個個人絕對可以看作是上面兩個的基礎,它的作用是確保 ref, 在函式內部永遠唯一,不管函式執行多少次。
      一句話,hook 函式的目的只有一個,就是確保函式在 React setState 多次時,有些變數能夠被共享,相當於提供了一個上下文。
      我們來看一下FC的型別定義。
/**
 * @deprecated as of recent React versions, function components can no
 * longer be considered 'stateless'. Please use `FunctionComponent` instead.
 *
 * @see [React Hooks](https://reactjs.org/docs/hooks-intro.html)
 */
type StatelessComponent<P = {}> = FunctionComponent<P>;

type FC<P = {}> = FunctionComponent<P>;

interface FunctionComponent<P = {}> {
  (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
  propTypes?: WeakValidationMap<P>;
  contextTypes?: ValidationMap<any>;
  defaultProps?: Partial<P>;
  displayName?: string;
}

  • React.ForwardRefRenderFunction<TRef,P> 以及著名的 React.forwardRef<TRef,P>() 它是用來定義元件,同時將 ref 傳遞給內部的元件,因為一般情況下,ref 會被賦予當前元件的值,而這個方法的作用就是洞穿當前元件,將 ref 往下傳遞。 我們來看一下型別定義,很清楚.forwardRef() 返回的其實是一個方法,同時含有一下屬性.
interface ForwardRefRenderFunction<T, P = {}> {
  (props: PropsWithChildren<P>, ref: ForwardedRef<T>): ReactElement | null;
  displayName?: string;
  // explicit rejected with `never` required due to
  // https://github.com/microsoft/TypeScript/issues/36826
  /**
   * defaultProps are not supported on render functions
   */
  defaultProps?: never;
  /**
   * propTypes are not supported on render functions
   */
  propTypes?: never;
}

function forwardRef<T, P = {}>(
  render: ForwardRefRenderFunction<T, P>
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;

// will show `ForwardRef(${Component.displayName || Component.name})` in devtools by default,
// but can be given its own specific name
interface ForwardRefExoticComponent<P> extends NamedExoticComponent<P> {
  defaultProps?: Partial<P>;
  propTypes?: WeakValidationMap<P>;
}
interface NamedExoticComponent<P = {}> extends ExoticComponent<P> {
  displayName?: string;
}

// TODO: similar to how Fragment is actually a symbol, the values returned from createContext,
// forwardRef and memo are actually objects that are treated specially by the renderer; see:
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/ReactContext.js#L35-L48
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/forwardRef.js#L42-L45
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/memo.js#L27-L31
// However, we have no way of telling the JSX parser that it's a JSX element type or its props other than
// by pretending to be a normal component.
//
// We don't just use ComponentType or SFC types because you are not supposed to attach statics to this
// object, but rather to the original function.
interface ExoticComponent<P = {}> {
  /**
   * **NOTE**: Exotic components are not callable.
   */
  (props: P): ReactElement | null;
  readonly $$typeof: symbol;
}

  • memo() vs React.useMemo() vs React.PureComponent React.memo() 它是一個 HOC,只有當內部元件的輸入引數變化了,或者內部元件使用了 useState,useReducer,useContext 這些 hook 函式,它才會觸發 render. 否則就直接返回上次 render 的內部元件.
    簡單的說,memo()函式執行後返回的是內部元件,跟內部元件幾乎一模一樣,只是,它的更新規則變了。同時它的比較還是淺比較。它屬於 HOC。
    useMemo() 它是 hook 函式,它將返回的值進行快取,除非以來項發生變化,否則值不變。它本身是一個函式。
    這裡還要提到 useCallback(), 跟 useMemo() 的唯一差別是它快取的是函式。通常用於事件響應函式的快取等。
    PureComponent, 它跟普通的 Component 的差別是預設實現了 componentshouldupdate() 方法,跟 memo() 類似。
function memo<T extends ComponentType<any>>(
  Component: T,
  propsAreEqual?: (
    prevProps: Readonly<ComponentProps<T>>,
    nextProps: Readonly<ComponentProps<T>>
  ) => boolean
): MemoExoticComponent<T>;

// will show `Memo(${Component.displayName || Component.name})` in devtools by default,
// but can be given its own specific name
type MemoExoticComponent<T extends ComponentType<any>> = NamedExoticComponent<
  ComponentPropsWithRef<T>
> & {
  readonly type: T;
};

// I made 'inputs' required here and in useMemo as there's no point to memoizing without the memoization key
// useCallback(X) is identical to just using X, useMemo(() => Y) is identical to just using Y.
/**
 * `useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
 * has changed.
 *
 * @version 16.8.0
 * @see https://reactjs.org/docs/hooks-reference.html#usecallback
 */
// TODO (TypeScript 3.0): <T extends (...args: never[]) => unknown>
function useCallback<T extends (...args: any[]) => any>(
  callback: T,
  deps: DependencyList
): T;
/**
 * `useMemo` will only recompute the memoized value when one of the `deps` has changed.
 *
 * Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in
 * the second argument.
 *
 * ```ts
 * function expensive () { ... }
 *
 * function Component () {
 *   const expensiveResult = useMemo(expensive, [expensive])
 *   return ...
 * }
 * ```
 *
 * @version 16.8.0
 * @see https://reactjs.org/docs/hooks-reference.html#usememo
 */
// allow undefined, but don't make it optional as that is very likely a mistake
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;

class PureComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> {}

  • React.cloneElement()