In React, using TypeScript, how do I pass a ref to a custom component using RefForwardingComponent and forwardRef?
The problem was a bad type definition for RefComp
. Removing that (letting TypeScript infer the type instead) and the code works just fine.
import * as React from 'react';
import {useRef, useState, forwardRef} from 'react';
interface Props {
value: string;
onChange(event: string): void;
}
const RefComp = forwardRef<HTMLInputElement, Props>(
({value, onChange}, ref) => (
<input
value={value}
onChange={event => onChange(event.target.value)}
ref={ref}
/>
),
);
export default function App() {
const [val, setVal] = useState('');
const inputRef = useRef<HTMLInputElement>(null);
React.useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return (
<div className="App">
<RefComp value={val} onChange={setVal} ref={inputRef} />
<p>{val}</p>
</div>
);
}
https://codesandbox.io/s/quizzical-liskov-bsruh
Related videos on Youtube
Comments
-
Tobbe over 1 year
I'm trying to pass a ref to a custom component (so that I can set focus to the component).
But I keep getting this error
const RefComp: React.RefForwardingComponent<HTMLInputElement, Props> Type '{ value: string; onChange: Dispatch<SetStateAction<string>>; ref: MutableRefObject<HTMLInputElement | undefined>; }' is not assignable to type 'IntrinsicAttributes & Props & { children?: ReactNode; }'. Property 'ref' does not exist on type 'IntrinsicAttributes & Props & { children?: ReactNode; }'.ts(2322)
This is my code
import * as React from 'react'; import {useRef, useState, RefForwardingComponent, forwardRef} from 'react'; interface Props { value: string; onChange(event: string): void; } const RefComp: RefForwardingComponent<HTMLInputElement, Props> = forwardRef< HTMLInputElement, Props >(({value, onChange}, ref) => ( <input value={value} onChange={event => onChange(event.target.value)} ref={ref} /> )); export default function App() { const [val, setVal] = useState(''); const inputRef = useRef<HTMLInputElement>(); return ( <div className="App"> <RefComp value={val} onChange={setVal} ref={inputRef} /> <p>{val}</p> </div> ); }
Here's a codesandbox https://codesandbox.io/s/crimson-cdn-klfp5
The code seems to work fine, if I ignore the error. So not sure why I'm getting it...
Can anyone please explain why I get the error, and how I fix it? :)
EDIT:
As mentioned in the comments you can work around this by adding
ref
to your props.import * as React from 'react'; import { useRef, useState, RefForwardingComponent, forwardRef, MutableRefObject, } from 'react'; interface Props { value: string; onChange(event: string): void; ref?: MutableRefObject<HTMLInputElement | null>; } const RefComp: RefForwardingComponent<HTMLInputElement, Props> = forwardRef< HTMLInputElement, Props >(({value, onChange}, ref) => ( <input value={value} onChange={event => onChange(event.target.value)} ref={ref} /> )); export default function App() { const [val, setVal] = useState(''); const inputRef = useRef<HTMLInputElement>(null); return ( <div className="App"> <RefComp value={val} onChange={setVal} ref={inputRef} /> <p>{val}</p> </div> ); }
But this feels wrong. Now we're saying we have
ref
both as parts of the props, and as a second callback argument to the function passed toforwardRef
My issue with this would probably be clearer if I wrote it like this
const RefComp: RefForwardingComponent<HTMLInputElement, Props> = forwardRef< HTMLInputElement, Props >((props: Props, ref) => ( <input value={props.value} onChange={event => props.onChange(event.target.value)} ref={ref /* here, props.ref is also available, but wouldn't work */ } /> ));
EDIT 2:
Here's a related question
Typescript RefForwardingComponent not working
EDIT 3:
I found the relevant source code. https://github.com/DefinitelyTyped/DefinitelyTyped/blob/33f6179e0f25b0ca798ad89a667c0a27ea0c98dd/types/react/index.d.ts#L702
function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;
So the
forwardRef
function returns a component of typeForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>
. This is a component that takes props of typePropsWithoutRef<P>
intersected withRefAttributes<T>
. Both types are also defined in the same file.From what I understand the first one basically just excludes
ref
from P (which isProps
in my original example). The second one is defined asinterface RefAttributes<T> extends Attributes { ref?: Ref<T>; }
Given this information I really don't understand why I have to add
ref
to my ownProps
. It really looks as ifforwardRef
should do it for me - even going as far as actually removingref
from my ownProps
...Can someone please explain what's going on here?
-
keikai over 4 yearsDirectly add ref declaration to
interface Props
seems work well
-