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
));
(над)