Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Component with forwarded ref. #106

Closed
andrewgreenh opened this issue Apr 15, 2019 · 3 comments · Fixed by #275
Closed

Generic Component with forwarded ref. #106

andrewgreenh opened this issue Apr 15, 2019 · 3 comments · Fixed by #275
Labels
wontfix This will not be worked on

Comments

@andrewgreenh
Copy link
Contributor

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} />;
@ferdaber
Copy link
Collaborator

ferdaber commented Apr 15, 2019

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
}

@eps1lon
Copy link
Member

eps1lon commented Apr 15, 2019

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.

@stale
Copy link

stale bot commented Aug 20, 2020

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wontfix This will not be worked on
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants