The opening

Those of you who have used the React stack have used elements passed from ref to render, and when using React to encapsulate components, there is a scenario like this:

The component uses props. Children as the render content; The ref is created inside the component and bound to props. Children.

We know that only one ref attribute reference can be bound to an element, but for this scenario, there might already be a ref attribute on props. Children, and the ref defined inside the component would be bound to props. Children.

We need to think of a way to apply both refs to elements.

Train of thought

First let’s review how React creates a ref:

  • React. CreateRef () : React 16.3 provides a class ref creation method;
  • React.useref () : react-hooks function components create refs;
  • Refs callback: Passes a function as the ref attribute of an element that takes either a React component instance or an HTML DOM element as an argument.

All things considered, since the Refs callback allows us to pass a function and take an element instance as an argument to that function, we can define such a function and write our logic within the function to handle multiple instances of the ref bound element. (Function flexibility)

implementation

  • Write a function (closure function) that acceptsprops.children.refComponents within the refAs a parameter;
  • The function (closure function) is requiredreturnReturns a function that will act asThe callback RefsTo act on an element;
  • inreturnBinds function parameters (element references) toprops.children.refComponents within the refOn.

The code:

function forkRef(refA, refB) {
  return refValue= > {
    setRef(refA, refValue);
    setRef(refB, refValue);
  };
}

function setRef(ref, value) {
  if (typeof ref === 'function') {
    ref(value);
  } else if(ref) { ref.current = value; }}Copy the code

CreateRef and react. useRef create a ref that has a current attribute.

Use:

const nodeRef = React.useRef(null); // Ref inside the component

const handleRef = forkRef(props.children.ref, nodeRef);

const childrenProps = { ref: handleRef };

return React.cloneElement(children, childrenProps);
Copy the code

Custom hook-useforkref

In the Hook component, we can use react.Memo () to optimize forkRef() logic to avoid creating a new closure every time the component is updated.

Here we write a useForkRef using TS:

import * as React from 'react';

interface MutableRefObject<T> {
  current: T;
}

type Ref<T> = ((instance: T | null) = > void) | MutableRefObject<T> | null;

export function setRef(ref: Ref<unknown>, value: unknown) {
  if (typeof ref === 'function') {
    ref(value);
  } else if(ref) { ref.current = value; }}export default function useForkRef(refA: Ref<unknown>, refB: Ref<unknown>) {
  return React.useMemo(() = > {
    if (refA == null && refB == null) {
      return null;
    }
    return (refValue: unknown) = > {
      setRef(refA, refValue);
      setRef(refB, refValue);
    };
  }, [refA, refB]);
}
Copy the code

Use:

const nodeRef = React.useRef<HTMLElement>(null); // Ref inside the component
const handleRef = useForkRef(children.ref, nodeRef);

const childrenProps: any = { ref: handleRef };
React.cloneElement(children, childrenProps)
Copy the code