Reputation: 1430
Using @types/react 16.8.2 and TypeScript 3.3.1.
I lifted this forward refs example straight from the React documentation and added a couple type parameters:
const FancyButton = React.forwardRef<HTMLButtonElement>((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef<HTMLButtonElement>();
<FancyButton ref={ref}>Click me!</FancyButton>;
I get the following error in the last line under FancyButton
:
Type '
{ children: string; ref: RefObject<HTMLButtonElement>; }
' is not assignable to type 'IntrinsicAttributes & RefAttributes<HTMLButtonElement>
'. Property 'children
' does not exist on type 'IntrinsicAttributes & RefAttributes<HTMLButtonElement>
'.ts(2322)
It would seem that the type definition for React.forwardRef's return value is wrong, not merging in the children prop properly. If I make <FancyButton>
self-closing, the error goes away. The lack of search results for this error leads me to believe I'm missing something obvious.
Upvotes: 140
Views: 193612
Reputation: 320
simplistic way to use forward-ref in typescript along with custom props
import { forwardRef, Ref, useRef } from 'react';
interface CustomInputType {
labelText: string;
}
const CustomInput = forwardRef(function (
{ labelText }: CustomInputType,
ref: Ref<HTMLInputElement>,
) {
return (
<section>
<label htmlFor="">{labelText}</label>
<input ref={ref} name="custom-input" type="text" />
</section>
);
});
function ForwardRefExample() {
const ref = useRef<HTMLInputElement>(null);
return <CustomInput ref={ref} labelText="student name" />;
}
Upvotes: 0
Reputation: 2852
You need to pass the button properties:
import * as React from 'react'
type ButtonProps = React.HTMLProps<HTMLButtonElement>
const FancyButton = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => (
<button type="button" ref={ref} className="FancyButton">
{props.children}
</button>
))
// You can now get a ref directly to the DOM button:
const ref = React.createRef<HTMLButtonElement>()
<FancyButton ref={ref}>Click me!</FancyButton>
ADDED:
In recent versions of TS and @types/react, you can also use React.ComponentPropsWithoutRef<'button'>
instead of React.HTMLProps<HTMLButtonElement>
Upvotes: 197
Reputation: 1
I had the same issue with javascript where I was not able to get the types of props for the autocompletion,
I used the following trick to make the completion work.
const _false = false;
export const MyComponent =
(_false ? ({ label }) => null : _false) || forwardRef((data, ref) => <input />);
Upvotes: -1
Reputation: 107
You can use ForwardRefRenderFunction<YourRefType, YourProps>
on your component.
Like:
const Component: ForwardRefRenderFunction<YourRef, YourProps> = (yourProps, yourRef) => return <></>
export default fowardRef(Component)
Upvotes: 6
Reputation: 859
The answers given by aMarCruz and euvs both work, but they lie to consumers a little bit. They say they accept all HTMLButtonElement props, but they ignore them instead of forwarding them to the button. If you're just trying to merge in the children prop correctly, then you might want to use React.PropsWithChildren instead:
import React from 'react';
interface FancyButtonProps {
fooBar?: string; // my custom prop
}
const FancyButton = React.forwardRef<HTMLButtonElement, React.PropsWithChildren<FancyButtonProps>>((props, ref) => (
<button type="button" ref={ref} className="fancy-button">
{props.children}
{props.fooBar}
</button>
));
FancyButton.displayName = 'FancyButton';
Or explicitly add a children prop:
interface FancyButtonProps {
children?: React.ReactNode;
fooBar?: string; // my custom prop
}
const FancyButton = React.forwardRef<HTMLButtonElement, FancyButtonProps>((props, ref) => (
<button type="button" ref={ref} className="fancy-button">
{props.children}
{props.fooBar}
</button>
));
FancyButton.displayName = 'FancyButton';
Or if you actually want to accept all the button props and forward them (let consumers choose button type="submit", for example), then you might want to use rest/spread:
import React from 'react';
interface FancyButtonProps extends React.ComponentPropsWithoutRef<'button'> {
fooBar?: string; // my custom prop
}
const FancyButton = React.forwardRef<HTMLButtonElement, FancyButtonProps>(
({ children, className = '', fooBar, ...buttonProps }, ref) => (
<button {...buttonProps} className={`fancy-button ${className}`} ref={ref}>
{children}
{fooBar}
</button>
),
);
FancyButton.displayName = 'FancyButton';
Upvotes: 59
Reputation: 1645
The answer given by aMarCruz works well. However, if you also need to pass custom props to the FancyButton
, here is how it can be done.
interface FancyButtonProps extends React.ComponentPropsWithoutRef<'button'> {
fooBar?: string; // my custom prop
}
const FancyButton = React.forwardRef<HTMLButtonElement, FancyButtonProps>((props, ref) => (
<button type="button" ref={ref} className="FancyButton">
{props.children}
{props.fooBar}
</button>
));
/// Use later
// You can now get a ref directly to the DOM button:
const ref = React.createRef<HTMLButtonElement>()
<FancyButton ref={ref} fooBar="someValue">Click me!</FancyButton>
Just adding here for completion.
Upvotes: 16