The simplest way is to create a ref callback that assigns the value to both of the refs.
The best thing about this method is that it follows the way React normally assigns refs.
Here's my utility function for it:
import type {MutableRefObject, RefCallback} from 'react';
type RefType<T> = MutableRefObject<T> | RefCallback<T> | null;
export const shareRef = <T>(refA: RefType<T>, refB: RefType<T>): RefCallback<T> => instance =>
{
if (typeof refA === 'function')
{
refA(instance);
}
else if (refA)
{
refA.current = instance;
}
if (typeof refB === 'function')
{
refB(instance);
}
else if (refB)
{
refB.current = instance;
}
};
Vanilla JavaScript:
export const shareRef = (refA, refB) => instance =>
{
if (typeof refA === 'function')
{
refA(instance);
}
else if (refA)
{
refA.current = instance;
}
if (typeof refB === 'function')
{
refB(instance);
}
else if (refB)
{
refB.current = instance;
}
};
All you have to do is use this utility function when assigning refs to components:
const MyComponent = forwardRef(function MyComponent(props, forwardedRef)
{
const localRef = useRef();
return (
<div ref={shareRef(localRef, forwardedRef)}/>
);
});
I'm fairly sure you'll have at most 2 different refs, but just in case I'll leave this version that supports any number of refs:
import type {MutableRefObject, RefCallback} from 'react';
type RefType<T> = MutableRefObject<T> | RefCallback<T> | null;
export const shareRef = <T>(...refs: RefType<T>[]): RefCallback<T> => instance =>
{
for (const ref of refs)
{
if (typeof ref === 'function')
{
ref(instance);
}
else if (ref)
{
ref.current = instance;
}
}
};
One thing to note here is that this callback is called on every render, which also means that if forwarded ref is a function it will also be called. This should not impact performance in any way, unless the forwarded ref function has some big performance overhead, which is something that rarely happens.
If you want to avoid the ref callback being called unnecessarily you can memorize it using useCallback:
import {MutableRefObject, RefCallback, useCallback} from 'react';
type RefType<T> = MutableRefObject<T> | RefCallback<T> | null;
export const useSharedRef = <T>(refA: RefType<T>, refB: RefType<T>): RefCallback<T> => useCallback(instance =>
{
if (typeof refA === 'function')
{
refA(instance);
}
else if (refA)
{
refA.current = instance;
}
if (typeof refB === 'function')
{
refB(instance);
}
else if (refB)
{
refB.current = instance;
}
}, [refA, refB]);
Since this involves hooks the usage is a bit different:
const MyComponent = forwardRef(function MyComponent(props, forwardedRef)
{
const localRef = useRef();
const sharedRef = useSharedRef(localRef, forwardedRef);
return (
<div ref={sharedRef}/>
);
});