React - Forwarding multiple refs
I actually just picked this up from react-hook-form, but they presented a nice way to share ref and assign multiple refs at once:
<input name="firstName" ref={(e) => {
register(e) // use ref for register function
firstNameRef.current = e // and you can still assign to ref
}} />
I know there is an already accepted answer, and while I find @nicholas-haley's solution acceptable.
I think a better way to go about it would be to use the built-in useImperativeHandle
hook.
IMPORTANT: The React Hooks Api is available as of
- [email protected] and later
- [email protected] and later
The React hooks API Docs state:
useImperativeHandle
customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with `forwardRef
This note is followed by the following example:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
Thus, in my opinion, a much cleaner solution would be to delegate the needed refs through the useImperativeHandle
hook.
This way there is no need for a special ref syntax, and the component can simply return a specific type of a FatherRef; Example:
// LabelInput.js
function LabelInput(props, ref) {
const labelRef = useRef();
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
get input() {
return inputRef.current;
},
get label() {
return labelRef.current;
},
// ... whatever else one may need
}));
return (
<div>
<label ref={labelRef} ... />
<input ref={inputRef} ... />;
</div>
)
}
LabelInput = forwardRef(LabelInput);
function MyScreen() {
const labelInputRef = useRef();
const onClick = useCallback(
() => {
// labelInputRef.current.focus(); // works
// labelInputRef.current.input.focus(); // works
// ... etc
},
[]
);
return (
...
<LabelInput ref={labelInputRef} ... />
....
)
}
I had a similar situation where I needed multiple refs to be forwarded to the child of my Parent component.
I still have not found an elegant solution, however you might try passing your refs as an object, and destructuring within the forwardRef
callback:
// Parent
ref={{
ref1: this.ref1,
ref2: this.ref2
}}
// Child
export default React.forwardRef((props, ref) => {
const { ref1, ref2 } = ref;
return (
<Child1
{...props}
ref={ref1}
/>
<Child2
{...props}
ref={ref2}
/>
);
});
I'm not a big fan of the naming here (I'd prefer ref
to be called refs
), but this works in case you're in a pinch.
EDIT:
In 2020 I believe @samer-murad's answer is the best solution to this problem.