React component pass down props to children

50 Views Asked by At

normally i work a lot in Vue.js and we have a api called scoped slots. Basically what it comes do to is that we can pass a slot to a component and use the data from the parent in the slot, which would also show what data is being passed via Typescript.

My problem is that i want to create a combobox select which as default shows a normal list as items. But via generics it could pass a complete other object as long as it has the ID and Label properties. I would like to also pass a component via the prop which it should render inside a for loop which can access the option he needs to render.

import { cloneElement } from "react";
import { Listbox } from "@headlessui/react";
import { CheckIcon } from "@heroicons/react/20/solid";

export type ListboxOptionBase = {
  label: string;
  id: string;
};

type Props<TOpion> = {
  listItem?: React.ReactElement;
  options: TOpion[];
};

type SelectOptionProps<T extends ListboxOptionBase = ListboxOptionBase> = {
  option: T;
  selected: boolean;
  active: boolean;
};

export function selectOption({ option, selected, active }: SelectOptionProps) {
  return (
    <div>
      {option.label}

      {selected ? <CheckIcon className="h-5 w-5" aria-hidden="true" /> : null}
    </div>
  );
}

// Spreading props will lose types for multiple: true|false for the value to be an array or not
export default function Select<T extends ListboxOptionBase>(props: Props<T>) {
  const renderElement = ({ active, selected, option }: SelectOptionProps) => {
    return cloneElement(props.listItem ?? selectOption, {
      option,
      selected,
      active,
    });
  };

  return props.options.map((option) => (
    <Listbox.Option key={option.id} value={option.id}>
      {({ selected, active }) => renderElement({ option, selected, active })}
    </Listbox.Option>
  ));
}

If i dont pass anything like this

<Select options={options} />

it should render the selectOption

But here i want to provide it with a custom selectOption which also holds different data.

type CustomOption = ListboxOptionBase & { image: string };

export function customSelectOption({ option, selected, active }: SelectOptionProps<CustomOption> {
  return (
    <div>
      {option.image}
      {option.label}
      {selected ? <CheckIcon className="h-5 w-5" aria-hidden="true" /> : null}
    </div>
  );
}

<Select options={options} listItem={customSelectOption}/>

Is something like this even doable?

Above code is what i tried but no luck so far.

0

There are 0 best solutions below