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() 一般,我们如果希望改变一个组件变量的输入参数或者 ref,我们就会调用它。因为我们不知道它内容是什么,所以需要这个方法,特别是写一个同样组件时,希望给传进来的组件添加一些额外功能的时候,类似与 Angular 里面的 TemplateRef<any>.
  • ReactDom.render(), 这个方法是将一个 React 组件插入到 React Virtual Dom 树上,类似与 Angular 里面的 AppRef.AttachRef()。这个很厉害的,可以通过编程的方式动态添加组件。
posted @ 2021-06-22 22:51  kongshu  阅读(1068)  评论(0编辑  收藏  举报