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
- react@16.8.0 and later
- react-native@0.59.0 and later
The React hooks API Docs state:
useImperativeHandlecustomizes 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} ... />
....
)
}