mTv
mTv

Reputation: 1356

How to create custom components for Virtuoso?

I am using React Virtuoso to create a virtual list. By default, React Virtuoso renders its list as a <div> with <div> elements. I would like to instead render a <ul> with <li> elements and add some styling like so:

const Item = React.forwardRef<HTMLLIElement, { children: React.ReactNode }>(
  (props, ref) => {
    return (
      <li className="border-r border-neutral-20" {...props} ref={ref}>
        {props.children}
      </li>
    );
  }
);

const List = React.forwardRef<HTMLUListElement, { children: React.ReactNode }>(
  (props, ref) => {
    return (
      <ul className="list-none divide-y divide-neutral-20" {...props} ref={ref}>
        {props.children}
      </ul>
    );
  }
);

const MyListComponent: React.FC = () => {
  return (
    <Virtuoso
      ...
      components={{ List, Item }}
      itemContent={(index: number) => {
      ...

It works but I am getting a TypeScript error saying that List and Item is of the wrong types. In the examples on their site they are using styled elements from emotion but I have no clue what types these have.

Does anyone know how to do this properly?

Here is the type-error if it is of any use:

Type 'ForwardRefExoticComponent<{ children: ReactNode; } & RefAttributes<HTMLUListElement>>' is not assignable to type 'ComponentType<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; }, "ref" | ... 1 more ... | "style"> & { ...; } & { ...; }> | undefined'.
  Type 'ForwardRefExoticComponent<{ children: ReactNode; } & RefAttributes<HTMLUListElement>>' is not assignable to type 'FunctionComponent<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; }, "ref" | ... 1 more ... | "style"> & { ...; } & { ...; }>'.
    Types of property 'propTypes' are incompatible.
      Type 'WeakValidationMap<{ children: ReactNode; } & RefAttributes<HTMLUListElement>> | undefined' is not assignable to type 'WeakValidationMap<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; }, "ref" | ... 1 more ... | "style"> & { ...; } & { ...; }> | undefined'.
        Type 'WeakValidationMap<{ children: ReactNode; } & RefAttributes<HTMLUListElement>>' is not assignable to type 'WeakValidationMap<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; }, "ref" | ... 1 more ... | "style"> & { ...; } & { ...; }>'.
          Types of property 'ref' are incompatible.
            Type 'Validator<Ref<HTMLUListElement> | undefined> | undefined' is not assignable to type 'Validator<((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined> | undefined'.
              Type 'Validator<Ref<HTMLUListElement> | undefined>' is not assignable to type 'Validator<((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined>'.
                Type 'Ref<HTMLUListElement> | undefined' is not assignable to type '((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined'.
                  Type '(instance: HTMLUListElement | null) => void' is not assignable to type '((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined'.
                    Type '(instance: HTMLUListElement | null) => void' is not assignable to type '(instance: HTMLDivElement | null) => void'.
                      Types of parameters 'instance' and 'instance' are incompatible.
                        Type 'HTMLDivElement | null' is not assignable to type 'HTMLUListElement | null'.
                          Type 'HTMLDivElement' is missing the following properties from type 'HTMLUListElement': compact, typets(2322)

Upvotes: 3

Views: 2245

Answers (1)

mTv
mTv

Reputation: 1356

I was unable to ge the List element to accept a HTMLUListElement as ref-type so I went for regular divs instead. Item worked with HTMLLiElement though which was nice.

So my solution ended up like this:

const Item = React.forwardRef<
  HTMLDivElement,
  ItemProps & { context?: Context<unknown> }
>((props, ref) => {
  return (
    <div className="border-r border-neutral-20" {...props} ref={ref}>
      {props.children}
    </div>
  );
});

const List = React.forwardRef<
  HTMLDivElement,
  ListProps & { context?: Context<unknown> }
>((props, ref) => {
  return (
    <div className="divide-y divide-neutral-20" {...props} ref={ref}>
      {props.children}
    </div>
  );
});

Upvotes: 3

Related Questions