Generic Component with forwarded ref.
andrewgreenh opened this issue ยท 3 comments
Hello everyone,
today I stumbled across the following problem:
I want to build a List Component that has a generic prop . From the outside you can pass a ref to the List, to access a scrollToItem(item: TItem). Since I want to use hooks, I need to use React.forwardRef, which does not allow to return a generic function.
This is my attempt of solving, but I feel like there could be something less verbose and repetitive.
type ListRef<ItemType> = {
scrollToItem: (item: ItemType) => void;
};
type ListProps<ItemType> = {
items: ItemType[];
};
const List = forwardRef(function List<ItemType>(props: ListProps<ItemType>) {
useImperativeHandle<ListRef<ItemType>, ListRef<ItemType>>(ref, () => ({
scrollToItem: (item: ItemType) => undefined
}));
return null;
}) as <ItemType>(
p: ListProps<ItemType> & { ref: Ref<ListRef<ItemType>> }
) => ReactElement<any> | null;
let ref = useRef<ListRef<number>>(null);
<List items={[1, 2, 3]} ref={ref} />;
TypeScript's inability to retain free type parameters when doing higher order functional programming is unfortunate, this is really the best way to do it if you want to use forwardRef
or any other HOC/HOF.
One workaround I use is to just not use forwardRef
and just use the olden-days method of innerRef
as a prop instead. The React docs say to use useImperativeHandle
against formal refs only but that's not a hard constraint:
type ListProps<ItemType> = {
items: ItemType[]
innerRef?: React.Ref<{ scrollToItem(item: ItemType): void }>
}
function List<ItemType>(props: ListProps<ItemType>) {
useImperativeHandle(props.innerRef, () => ({
scrollToItem() { }
}))
return null
}
The React docs say to use useImperativeHandle against formal refs only but that's not a hard constraint:
useImperativeHandle should be used with forwardRef:
-- https://reactjs.org/docs/hooks-reference.html#useimperativehandle
This is more of a recommendation so that you don't leak more implementation detail than necessary. Depending on the use case it might make more sense to use the default ref
for a host instance and some actions
or handle
prop for useImperativeHandle
.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions!