skip to Main Content

I tried to create a reusable swiper component with Next.js 13 (App Router) v13.4.12 and also use TypeScript. I get an error when I try to "consume" data from the component props, which is a custom function render for the parent.

First I create the interface IItem & ICarousell like this also use generic for reusable

import { ReactNode } from "react";

export interface IItem {
  [key: string]: undefined
}

export interface ICarousell<T> {
  items: Array<T | IItem>,
  children?: ReactNode,
  customRender?(el: IItem | T): T | ReactNode
}

Then I created the reusable Swiper component like this, using the Interface that I created before.

"use client"
import { Swiper, SwiperSlide } from "swiper/react"
import { ICarousell } from "./Carousell.types";

import 'swiper/css';

export default function Carousell<T> ({ children, items, customRender, ...props }: ICarousell<T>) {
  return (
    <Swiper
      {...props}
    >
      {items.map((item, idx) => (
        <SwiperSlide key={idx}>
          <>
            {customRender ? customRender(item) : item }
          </>
        </SwiperSlide>
      ))}

      {children}
    </Swiper>
  )
}

After that, I tried to use the Carousell component on the Home page, but I got an error similar to the image I attached below.

import { Carousell } from "@/core/components";

export default function Home() {
  return (
    <main>
      <Carousell
        items={[
          1,
        ]}
        customRender={(element) => (
          <div>{element}</div>
        )}
      />
    </main>
  )
}

Error Sample

And I tried to edit my generic just for testing like this

export interface ICarousell<T> {
  items: Array<T | IItem>,
  children?: ReactNode,
  customRender?(el: any): ReactNode // Use "any" for the parameter type
}

I tried not to change the custom render type with any, and I tried using a React fragment tag, it worked.

<Carousell
  items={[
    1,
  ]}
  customRender={(element) => (
    <>{element}</>
  )}
/>

but if I tried again to add the div inside the fragment tag like this, it wouldn’t work again.

<Carousell
  items={[
    1,
  ]}
  customRender={(element) => (
    <>
      <div>{element}</div>
    </>
  )}
/>

I’m not sure, but I think the problem is the parameter type. I don’t know how to deal with it. Hopefully you guys can help me. Thanks in advance. Any feedback will be appreciated.

2

Answers


  1. Since you’re using arrow function on customRender

    You could modify ICarousel interface like this

    export interface ICarousell<T> {
      items: Array<T | IItem>,
      children?: ReactNode,
      customRender?: (el: IItem | T) => ReactNode
    }
    

    Reference: https://www.typescriptlang.org/docs/handbook/interfaces.html#function-types

    Login or Signup to reply.
  2. In ICarousell, the customRender is expected to return either T or ReactNode. But in usage you are returning a JSX element <div>{element}</div> which is of type ReactNode. I think this is conflicting with the type T you inferred – in this case it is a number (as the items array containing numbers).

    I would adjust the type definitions to make sure the customRender always returns a ReactNode since you are rendering result in JSX.

    ICarousell interface

    export interface ICarousell<T>{
      items: Array<T>,
      children?: ReactNode,
      customRender?(el: T): ReactNode
    }
    

    You dont need extra tags around the customRender 😉

    export default function Carousell<T>({children, items, customRender, ...props }: ICarousell<T>){
      return(
        <Swiper {...props}>
          {items.map((item, idx)=>(
            <SwiperSlide key={idx}>
              {customRender ? customRender(item) : item}
            </SwiperSlide>
          ))}
          {children}
        </Swiper>
      );
    }
    

    Usage

    <Carousell
      items={[1]}
      customRender={(element) => <div>{element}</div>}
    />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search