Сценарии применения и анализ исходного кода React.forwardRef

React.js

1. Сценарии применения React.forwardRef
Роль ref заключается в получении экземпляра, который может быть экземпляром DOM или экземпляром ClassComponent.

Но столкнулись со следующими проблемами:
① Если целевой компонент является FunctionComponent, экземпляра (PureComponent) нет.В настоящее время использование ref для его передачи сообщит об ошибке:

② Если вы являетесь разработчиком библиотеки, и человек, который использует библиотеку, не знает категорию компонента библиотеки, то, когда категория компонента библиотеки — FunctionComponent, что должен сделать пользователь, чтобы использовать ref для получения компонента библиотеки?

③ Метод connect в redux оборачивает компонент в компонент более высокого порядка (HOC), так как же мне получить экземпляр компонента перед упаковкой через ref?

④ реквизит не может передать рефери

React официально также заявил условия использования ref:

React.forwardRefЦель существования – решение вышеуказанных проблем.

оReact.forwardRefиспользовать, см.:

this-function.react JS.org/docs/react-…

Далее мы говоримReact.forwardRefисходный код

2. Реагировать.forwardRef
эффект:
Создайте компонент React, способный пересылать атрибут ref, который он получает, компоненту внутри

Исходный код:

export default function forwardRef<Props, ElementType: React$ElementType>(
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
  //删除了 dev 代码

  return {
    //被forwardRef包裹后,组件内部的?typeof是REACT_FORWARD_REF_TYPE
    ?typeof: REACT_FORWARD_REF_TYPE,
    //render即包装的FunctionComponent,ClassComponent是不用forwardRef的
    render,
  };
}

Разобрать:
(1) Возвращаемый объект — это следующая переменная Child:

const Child = React.forwardRef((props, ref) => (
  <button ref={ref}>
    {props.children}
  </button>
));

console.log(Child,'Child29')

(2) Внутри возвращаемого объекта?typeof, не указываетChildТип компонентаREACT_FORWARD_REF_TYPE

я писал раньшеReact.createElement() и ReactElement() анализа исходного кода ReactупомянулReactElementизtypeдляREACT_ELEMENT_TYPE:

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element
    //标识element的类型
    //因为jsx都是通过createElement创建的,所以ReactElement的类型固定:为REACT_ELEMENT_TYPE
    //重要!因为react最终渲染到DOM上时,需要判断?typeof===REACT_ELEMENT_TYPE
    ?typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    //设置元素的内置属性
    type: type,

  };
}

Уведомление:
① ПеременнаяChildявляется объектом:

{
    $$typeof: REACT_FORWARD_REF_TYPE,
    render,
  };

ChildКомпоненты — это объекты ReactElement,это не то же самое:

  const element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
  };

когда положитьChildПри преобразовании в ReactElement,Childобъект какChildкомпонентtypeАтрибуты:

  const element = {
    ?typeof: REACT_ELEMENT_TYPE,
    //注意!!!
    type: {
      ?typeof: REACT_FORWARD_REF_TYPE,
      render,
    },
  };

не хочусчитаетсяforwardRefПосле упаковки компонент React?typeofЗначение изменится наREACT_FORWARD_REF_TYPE!

② Можно использовать только FunctionComponentforwardRef, ClassComponent не нуждается

3. обновить форвардреф
эффект:
возобновитьrefнаправленный иReact.forwardRefзавернутыйFunctionComponent

Исходный код:

//更新被React.forwardRef包裹的 FunctionComponent
function updateForwardRef(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderExpirationTime: ExpirationTime,
) {
  // TODO: current can be non-null here even if the component
  // hasn't yet mounted. This happens after the first render suspends.
  // We'll need to figure out if this is fine or can cause issues.

  //删除了 dev 代码

  //Component:{
  //   ?typeof: REACT_FORWARD_REF_TYPE,
  //   render,
  // }

  //FunctionComponent
  const render = Component.render;
  // 开发层面上不允许FunctionComponent,但你打印 props 的话是有的,
  // 因为是 React 只允许内部通过 props 传进来 ref
  const ref = workInProgress.ref;

  // The rest is a fork of updateFunctionComponent
  let nextChildren;
  //context 相关的可跳过
  prepareToReadContext(workInProgress, renderExpirationTime);
  prepareToReadEventComponents(workInProgress);
  if (__DEV__) {
    //删除了 dev 代码
  } else {
    //渲染的过程中,对里面用到的 hook函数做一些操作
    //关于renderWithHooks的讲解,请看:https://www.jianshu.com/p/959498695e83

    //注意:在updateFunctionComponent()中传的参数不是 ref,
    //而是 context:nextChildren = renderWithHooks(
    //   current,
    //   workInProgress,
    //   Component,
    //   nextProps,
    //   传的是 context 而不是 ref
    //   context,
    //   renderExpirationTime,
    // );
    nextChildren = renderWithHooks(
      current,
      workInProgress,
      render,
      nextProps,
      ref,
      renderExpirationTime,
    );
    //renderWithHooks 内部通过let children = Component(props, refOrContext)来更新 ref 或 context
  }

  //如果 props 相同,并且 ref 也相同的话,就不需要更新
  if (current !== null && !didReceiveUpdate) {
    //跳过hooks更新
    //关于bailoutHooks的讲解,请看:https://www.jianshu.com/p/959498695e83
    bailoutHooks(current, workInProgress, renderExpirationTime);
    //跳过该节点及所有子节点的更新
    //关于bailoutOnAlreadyFinishedWork的讲解,请看:https://www.jianshu.com/p/06b18db8b5d4
    return bailoutOnAlreadyFinishedWork(
      current,
      workInProgress,
      renderExpirationTime,
    );
  }

  // React DevTools reads this flag.
  workInProgress.effectTag |= PerformedWork;
  //将 ReactElement 变成 fiber对象,并更新,生成对应 DOM 的实例,并挂载到真正的 DOM 节点上
  //关于reconcileChildren的讲解,请看:https://www.jianshu.com/p/959498695e83
  reconcileChildren(
    current,
    workInProgress,
    nextChildren,
    renderExpirationTime,
  );
  return workInProgress.child;
}

Разобрать:
(1) Логика относительно проста, и большинство функций в ней были разобраны в предыдущих статьях:
① О программеrenderWithHooks,bailoutHooks,reconcileChildrenДля объяснения см.:
FunctionComponent анализа исходного кода React (включено)

② О программеbailoutOnAlreadyFinishedWorkДля объяснения см.:
workLoop анализа исходного кода React

(2) Обновление ссылки находится вrenderWithHooksсередина:

let children = Component(props, refOrContext);

здесьComponentда二、React.forwardRefдочернего объекта вrenderсвойства, то есть для выполнения рендерингаFunctionComponentМетоды

refOrContextвотworkInProgress.ref

то естьComponent(props, refOrContext)ПараметрыReact.forwardRefпараметры в(props, ref):

React.forwardRef((props, ref) => (
  xxx
));

(над)