React/TypeScript 일반 구성 요소 만들기

종종 우리는 React/TypeScript에서 모든 종류의 유형을 허용해야 하는 일반 구성 요소를 만들어야 합니다.

우리는 재사용 가능한 구성 요소를 만들고 동시에 유형이 안전해야 하므로 자체 props를 any 유형으로 정의할 수 없으며 unknown는 유효한 솔루션이 아닌 경우가 많습니다.

이제 모든 유형의 array( TabBar , items , string[] )의 User[] 속성을 허용하는 React/TypeScript에서 Whatever[] 구성 요소를 만들어야 한다고 상상해 봅시다.

<TabBar
  items={anyTypeOfArray}
  onTabClick={selectHandler}
/>


출력:

TabBar items 속성이 모든 종류의 유형을 허용해야 하는 경우 any[] 를 사용할 수 있습니다. 오른쪽? 음...아니 😅
우리는 유형 검사를 완전히 잃어버렸습니다!

interface TabBarProps<T> {
  items: any[];
  selectedItem: any;
  onTabClick: (item: any, selectedIndex: number) => void
}


실제로 any 를 사용하면 TypeScript 컴파일러와 IDE/편집기가 어떤 유형의 매개변수onTabClick가 반환되는지 또는 어떤 유형의 데이터selectedItem가 허용되어야 하는지 알 수 없습니다.

해결책


any를 사용하는 대신 일반 유형을 구성 요소에 전달할 수 있습니다.

1) 먼저 사용자 지정 유형을 생성합니다(이 예에서는MySocial 그러나 무엇이든 될 수 있음).

interface MySocial {
  id: number;
  name: string;
  link: string;
}

const socials: MySocial[] = [
  { id: 11, name: 'WebSite', link: 'https://www.fabiobiondi.dev'},
  { id: 12, name: 'Youtube', link: 'https://www.youtube.com/c/FabioBiondi'},
  { id: 13, name: 'Twitch', link: 'https://www.twitch.tv/fabio_biondi'},
]


2) 이 유형을 일반으로 구성 요소에 전달할 수 있습니다.

<TabBar<MySocial>
  selectedItem={selectedSocial}
  items={socials}
  onTabClick={selectHandler}
/>


3) TabBar 구성 요소는 이제 any 대신 제네릭을 사용해야 합니다.
또한 이 유형이 정의에 idname를 포함해야 한다고 결정할 수 있습니다.

interface TabBarProps<T> {
  items: T[];
  selectedItem: T;
  onTabClick: (item: T, selectedIndex: number) => void
}

export function TabBar<T extends { id: number, name: string}>(props: TabBarProps<T>) {

  // ... your component code here ...


최종 소스 코드



다음은 TabBar의 전체 소스 코드입니다(CSS에 Tailwind를 사용하지만 중요하지 않음).

// TabBar.tsx
interface TabBarProps<T> {
  items: T[];
  selectedItem: T;
  onTabClick: (item: T, selectedIndex: number) => void
}

export function TabBar<T extends { id: number, name: string}>(props: TabBarProps<T>) {
  const { items, selectedItem, onTabClick} = props;
  return (
    <>
      <div className="flex gap-x-3">
        {
          items.map((item, index) => {
            const activeCls = item.id === selectedItem.id ? 'bg-slate-500 text-white' : ' bg-slate-200';
            return <div
                key={item.id}
                className={'py-2 px-4 rounded ' + activeCls}
                onClick={() => onTabClick(item, index)}
              >
                {item.name}
              </div>
            }
          )
        }
      </div>
    </>
  )
}



용법



다음은 사용 예입니다.

// App.tsx
import { useState } from 'react';
import { TabBar } from '../../../shared/components/TabBar';

interface MySocial {
  id: number;
  name: string;
  link: string;
}

const socials: MySocial[] = [
  { id: 11, name: 'WebSite', link: 'fabiobiondi.dev'},
  { id: 12, name: 'Youtube', link: 'YT'},
  { id: 13, name: 'Twitch', link: 'twitch'},
]

export const App = () => {
  const [selectedSocial, setSelectedSocial] = useState<MySocial>(socials[0])

  function selectHandler(item: MySocial, selectedIndex: number) {
    setSelectedSocial(item)
  }

  return (
    <div>
      <h1>Tabbar Demo</h1>
        <TabBar<MySocial>
          selectedItem={selectedSocial}
          items={socials}
          onTabClick={selectHandler}
        />

      <div className="border border-slate-200 border-solid rounded my-3 p-5">
        <a href={selectedSocial.link}>Visit {selectedSocial.name}</a>
      </div>
    </div>
  )
};



결과:





이 기사를 읽을 수도 있습니다.

좋은 웹페이지 즐겨찾기