How to use Custom Components as Props in rescript

I’m working with react-multi-carousel where i need to pass customDot in jsx it is written like below but I not sure how to do this in rescript

const CustomDot = ({ onMove, index, onClick, active }) => {
      return (
        <li
          className={active ? "active" : "inactive"}
          onClick={() => onClick()}
        >
          <MaximizeIcon />
        </li>
      );
    };

 <Carousel customDot={<CustomDot />} />

@zth @fham could you please help

Well the prop should just be typed as a React.element I guess then.

2 Likes

Like Paul is saying, if you’re putting the CustomDot in brackets as you are passing it into Carousel, you are doing the rendering of that component into React.element. You don’t pass the props to CustomDot there in your example so its unclear which way you want that to go.
If its the Carousel that applies onMove, index etc to CustomDot, you would say like…

module CustomDot = {
type props = {
  onClick: () => ()
}

let make = (props: props) => {
  React.null
  }
}

module Carousel = {
@react.component
  external make : (~customDot: CustomDot.props => React.element) => React.element = "Carousel"

}

<Carousel customDot=CustomDot.make/>
2 Likes

@tsnobip @mouton thanks for replying
I tried doing what @mouton suggest,
my current code -

module Carousel = {
  @module("react-multi-carousel") @react.component
  external make: (
    ~children: React.element,
    ~responsive: responsive,
    ~deviceType: string=?,
    ~ssr: bool=?,
    ~slidesToSlide: int=?,
    ~draggable: bool=?,
    ~arrows: bool=?,
    ~renderArrowsWhenDisabled: bool=?,
    ~swipeable: bool=?,
    ~removeArrowOnDeviceType: array<string>=?,
    ~customLeftArrow: React.element=?,
    ~customRightArrow: React.element=?,
    ~customDot: dotProps => React.element=?,
}


module CustomDots = {
  type props = ReactMultiCarousel.dotProps
  let make = (props: props) => {
    Js.log(props)
    <div className="flex rounded-full bg-[#E5E7EB] w-[3px] h-[3px]" />
  }
}

<ReactMultiCarousel
      customDot=CustomDots.make
      afterChange=onAfterChange>
      qrTickets
    </ReactMultiCarousel>

but it is giving error

Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
    at createFiberFromTypeAndProps (react-dom.development.js:28435:17)
    at createFiberFromElement (react-dom.development.js:28461:15)
    at createChild (react-dom.development.js:15110:28)
    at reconcileChildrenArray (react-dom.development.js:15405:25)
    at reconcileChildFibers (react-dom.development.js:15822:16)
    at reconcileChildren (react-dom.development.js:19163:28)
    at updateHostComponent (react-dom.development.js:19920:3)
    at beginWork (react-dom.development.js:21614:14)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4165:14)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4214:16)

for more context I’m giving my use case, so basically I’m using react-multi-carousel library and it has a prop customDom which takes jsx, in js it is implemented like this https://codesandbox.io/p/sandbox/react-multi-carousel-customdot-forked-ngppsl?file=%2Fsrc%2FApp.js%3A22%2C26-22%2C56

I’m not sure what we can use here, i tried React.cloneElement it also didn’t worked

What worked for me is


let defaultDotProps = {
  onMove: _ => (),
  index: 0,
  carouselState: defaultStateCallBack,
  onClick: _ => (),
  active: false,
}
module CustomDots = {
  type props<'dotsProps> = {dotsProps: 'dotsProps}
  let make = props => {...}
}
<ReactMultiCarousel customDot={CustomDots.make} >children </ReactMultiCarousel>

in ReactMultiCarousel component

let customDot = customDot->Belt.Option.map(customDot => %raw(`JsxRuntime.jsx(customDot, {})`))
  <>
making  ~customDot: Jsx.element=?,

so I think what was happening was when passing element it was appling property to dom which will be div so instead of passing element we can passing jsx component and inside library we are already calling cloneElement to pass the required element so this worked
but I’m not sure about the correctness of this solution even though it worked
@tsnobip @mouton is there any better way for doing this?

For generic components, I like to use the first class module type :
It allows to enforce styling rules based on component props.

// Button.res
module type Icon = {
  @react.component
  let make: (~size: int=?, ~color: string=?, ~className: string=?) => React.element
}

@react.component
let make = (
    ~leftIcon: option<module(Icon)>=?,
   ...
) => {
       {leftIcon->Option.mapWithDefault(React.null, icon => {
        let module(Icon) = icon

        <span>
          <Icon size={24}  />
        </span>
      })}

}

// ReactIcons.res
module Md3dRotation = {
  @module("react-icons/md") @react.component
  external make: (~size: int=?, ~color: string=?, ~className: string=?) => React.element =
    "Md3dRotation"
}

<Button leftIcon={module(Md3dRotation)} />
2 Likes