The ref gets an instance. It can be a DOM instance or a ClassComponent instance.
If the target FunctionComponent is a PureComponent, there is no instance (ref). If the target FunctionComponent is a FunctionComponent, there is no instance (ref).
If you are a library developer, the user does not know the library component class. If the FunctionComponent class is FunctionComponent and the user wants to retrieve the library component, what do you do?
(3) The connect method in Redux wraps a component as a high-level component (HOC), so how can I get a wrapped component instance through ref?
4 Props cannot pass ref
React also specifies the conditions for using ref:
The react. forwardRef is there to solve these problems.
React.forwardRef
Zh-hans.reactjs.org/docs/react-…
React.forwardRef (forwardRef
ForwardRef creates a React component that forwards the ref properties it receives to an internal component
Source:
export default function forwardRef<Props.ElementType: React$ElementType> (
render: (props: Props, ref: React$Ref<ElementType>) = >React$Node.
) {
// Delete dev code
return {
// After the forwardRef wraps the component? Typeof is REACT_FORWARD_REF_TYPE
?typeof: REACT_FORWARD_REF_TYPE,
//render is the FunctionComponent wrapper, ClassComponent is not forwardRef
render,
};
}
Copy the code
(1) return an object, Child:
const Child = React.forwardRef((props, ref) = > (
<button ref={ref}>
{props.children}
</button>
));
console.log(Child,'Child29')
Copy the code
(2) Return? Typeof, which does not indicate that the Child component type is REACT_FORWARD_REF_TYPE
React.createelement () and ReactElement() 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
// Identify the type of element
// Since JSX is created with createElement, ReactElement is of a fixed type: REACT_ELEMENT_TYPE
/ / important! Because react, when it finally renders to the DOM, you have to decide, okay? typeof===REACT_ELEMENT_TYPE
?typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
// Sets the built-in attributes of the element
type: type,
};
}
Copy the code
Note: the variable Child is an object:
{
$$typeof: REACT_FORWARD_REF_TYPE,
render,
};
Copy the code
The Child component is a ReactElement object, which is different:
const element = {
$$typeof: REACT_ELEMENT_TYPE,
type: type.
};
Copy the code
When a Child is converted to ReactElement, the Child object is used as the type property of the Child component:
const element = {
?typeof: REACT_ELEMENT_TYPE,
// Attention!!
type: {
?typeof: REACT_FORWARD_REF_TYPE,
render,
},
};
Copy the code
Do not think React component is wrapped by forwardRef? The value of typeof is changed to REACT_FORWARD_REF_TYPE!
The forwardRef is only available for FunctionComponent, not ClassComponent
Update the FunctionComponent that the ref points to and is wrapped by the react. forwardRef
Source:
// Update FunctionComponent wrapped by react. forwardRef
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.
// Delete dev code
//Component:{
/ /? typeof: REACT_FORWARD_REF_TYPE,
// render,
// }
//FunctionComponent
const render = Component.render;
// FunctionComponent is not allowed at the development level, but if you print props,
// React only allows internal ref passes through props
const ref = workInProgress.ref;
// The rest is a fork of updateFunctionComponent
let nextChildren;
// Context related can be skipped
prepareToReadContext(workInProgress, renderExpirationTime);
prepareToReadEventComponents(workInProgress);
if (__DEV__) {
// Delete dev code
} else {
// Perform some operations on the hook functions used in the rendering process
/ / about renderWithHooks's explanation, please see: https://www.jianshu.com/p/959498695e83
// Note that the argument passed in updateFunctionComponent() is not ref,
// Instead of context: nextChildren = renderWithHooks(
// current,
// workInProgress,
// Component,
// nextProps,
// Pass context instead of ref
// context,
// renderExpirationTime,
// );
nextChildren = renderWithHooks(
current,
workInProgress,
render,
nextProps,
ref.
renderExpirationTime,
);
//renderWithHooks update ref or context internally by let children = Component(props, refOrContext)
}
// If the props are the same and the ref is the same, no update is needed
if(current ! = =null && !didReceiveUpdate) {
// Skip the hooks update
/ / about bailoutHooks's explanation, please see: https://www.jianshu.com/p/959498695e83
bailoutHooks(current, workInProgress, renderExpirationTime);
// Skip the update of this node and all its children
/ / about bailoutOnAlreadyFinishedWork's explanation, please see: https://www.jianshu.com/p/06b18db8b5d4
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
// Change the ReactElement into a fiber object and update it to generate the corresponding DOM instance and mount it to the real DOM node
/ / about reconcileChildren's explanation, please see: https://www.jianshu.com/p/959498695e83
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
Copy the code
For the reconcileChildren, see renderWithHooks, bailoutHooks, and reconcileChildren. React FunctionComponent
(2) about the interpretation of bailoutOnAlreadyFinishedWork, see: the React workLoop source code parsing
(2) The ref update is in renderWithHooks:
let children = Component(props, refOrContext);
Copy the code
The FunctionComponent is the render property of the react. forwardRef Child, which executes the FunctionComponent
RefOrContext in this case is workinProgress.ref
Component(props, refOrContext) (props, ref)
React.forwardRef((props, ref)= > (
xxx
));
Copy the code
(after)