React Ref is considered to be an . How to do something with a ref parameter inside the component?
const Component = forwardRef<HTMLDivElement, unknown>((props, ref) => {
// 🚫 will not work!
useEffect(() => {
ref.current.focus()
}, []);
return <div ref={ref}>Hello, world!</div>;
});
Component.displayName = 'ComponentName';
This snippet is not possible for two reasons:
ref parameter may be undefined, when not provided by the developer.
We don't know what type of reference we are getting, it may be not just a Mutable Ref Object, but also a function (callback ref). Which doesn't have
currentproperty.
Here is a React hook which fixes the issue. It takes all possible user provided refs and always emits a Mutable Ref Object, thus providing us required consistency.
import type {ForwardedRef, MutableRefObject} from 'react';
import {useImperativeHandle, useRef} from 'react';
// generic type will be resolved by the compiler
const useNormalizeRef = <TElement>(
externalRef: ForwardedRef<TElement>
): MutableRefObject<TElement | null> => {
// create a Mutable Ref Object for internal use
const internalRef = useRef<TElement | null>(null);
// this hook connects internal and external references
useImperativeHandle<TElement | null, TElement | null>(
externalRef,
() => internalRef.current,
[]
);
return internalRef;
};
Here is how to use this hook
const Component = forwardRef<HTMLDivElement, unknown>((props, ref) => {
// create an internal clone of the prop with a consistent type
const internalRef = useNormalizeRef(ref);
useEffect(() => {
// access Mutable Ref Object normally
internalRef.current!.focus();
}, [internalRef]);
return <div ref={ref}>Hello, world!</div>;
});
Component.displayName = 'Component';
Linking
Sometimes we need to link two existing references instead of creating a new one. Here is how to implement it.
import type {ForwardedRef, MutableRefObject} from 'react';
import {useImperativeHandle} from 'react';
export const useLinkRefs = <TElement>(
externalRef: ForwardedRef<TElement>,
internalRef: MutableRefObject<TElement | null>
) => {
useImperativeHandle<TElement | null, TElement | null>(externalRef, () => internalRef.current, [
internalRef,
]);
};
Happy coding!
SOCIAL SHARE CARD GENERATOR