Created
November 20, 2022 07:38
-
-
Save caprica/554648586f9e35a204f82f0c281d1097 to your computer and use it in GitHub Desktop.
Example of a React component using Typescript with generics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* An outline example of how to create a generic component using Typescript and | |
* React. | |
* | |
* This is NOT the only way to do something like this. | |
*/ | |
// === Generic component === | |
/** | |
* All items in a Carousel must have a (unique) id. | |
* | |
* This interface is extended by concrete types. | |
*/ | |
export interface CarouselItem { | |
id: string | |
} | |
/** | |
* A Carousel component has a model containing an array of any type of Carousel | |
* Item and a component to render an individual item. | |
*/ | |
export interface CarouselProps<T extends CarouselItem> { | |
model: Array<T> | |
itemComponent: React.FC<CarouselItemProps<T>> | |
} | |
/** | |
* A Carousel Item component has a model of any type of single Carousel Item. | |
*/ | |
export interface CarouselItemProps<T extends CarouselItem> { | |
model: T | |
} | |
/** | |
* A generic Carousel component. | |
* | |
* @param param0 component properties | |
* @returns component | |
*/ | |
export const Carousel = <T extends CarouselItem>({ model, itemComponent: ItemComponent }: CarouselProps<T>) => { | |
return ( | |
<div> | |
{model.map(item => ( | |
<div key={item.id}> | |
<ItemComponent model={item} /> | |
</div> | |
))} | |
</div> | |
) | |
} | |
// === Custom component example === | |
interface MyModel extends CarouselItem { | |
name: string | |
} | |
const MyTile = ({ model }: CarouselItemProps<MyModel>) => { | |
return <div>{model.name}</div> | |
} | |
export const MyTestComponent = () => { | |
const model: MyModel[] = [ | |
{ | |
id: '1', | |
name: 'One' | |
}, | |
{ | |
id: '2', | |
name: 'Two' | |
} | |
] | |
/* | |
* You could declare the model type on the Carousel component, but | |
* Typescript can infer it from the model property. | |
*/ | |
// return <Carousel<MyModel> model={model} tileComponent={MyTile} /> | |
return <Carousel model={model} itemComponent={MyTile} /> | |
} | |
// === Another custom component example === | |
interface MyOtherModel extends CarouselItem { | |
title: string | |
description?: string | |
tags?: string[] | |
price: number | |
} | |
const MyOtherTile = ({ model }: CarouselItemProps<MyOtherModel>) => { | |
return ( | |
<div> | |
<h1>{model.title}</h1> | |
<h2>{model.description}</h2> | |
{model.tags && ( | |
<ul> | |
{model.tags.map(tag => ( | |
<li key={tag}>{tag}</li> | |
))} | |
</ul> | |
)} | |
<span>£{model.price}</span> | |
</div> | |
) | |
} | |
export const MyOtherTestComponent = () => { | |
const model: MyOtherModel[] = [ | |
{ | |
id: '1', | |
title: 'One', | |
description: 'Hey hey', | |
price: 100 | |
}, | |
{ | |
id: '2', | |
title: 'Two', | |
tags: ['A', 'B', 'C'], | |
price: 250 | |
} | |
] | |
/* | |
* You could declare the model type on the Carousel component, but | |
* Typescript can infer it from the model property. | |
*/ | |
// return <Carousel<MyOtherModel> model={model} tileComponent={MyOtherTile} /> | |
return <Carousel model={model} itemComponent={MyOtherTile} /> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment