0Pricing
React Academy · Lekcja

Komponenty generyczne i typy narzędziowe

Pisanie wielokrotnego użytku komponentów generycznych oraz używanie PropsWithChildren, ComponentProps i Omit.

Komponenty generyczne i typy narzędziowe to bezpłatna lekcja React Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej React Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs React Academy zawiera 4 lekcji w sumie.

Dlaczego komponenty generyczne?

Komponenty generyczne dostosowują się do różnych typów danych, zachowując pełne bezpieczeństwo typów — typowana lista, select lub tabela może działać z dowolnym kształtem danych.

Prosta generyczna lista

Należy użyć parametru typu T w funkcji, aby utworzyć komponent listy renderujący elementy dowolnego typu.

interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, i) => (
        <li key={keyExtractor(item)}>{renderItem(item, i)}</li>
      ))}
    </ul>
  );
}

Ograniczanie typów generycznych

Należy użyć extends, aby dodać ograniczenia i poinformować TypeScript, jakie właściwości musi mieć dany typ.

interface SelectProps<T extends { id: string; label: string }> {
  options: T[];
  value: T | null;
  onChange: (option: T) => void;
}

function Select<T extends { id: string; label: string }>(
  { options, value, onChange }: SelectProps<T>
) {
  return (
    <select value={value?.id} onChange={e => onChange(options.find(o => o.id === e.target.value)!)}>
      {options.map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
    </select>
  );
}

PropsWithChildren

React.PropsWithChildren<P> dodaje children?: React.ReactNode do dowolnego typu propsów — jest to przydatne w komponentach opakowujących.

function Card({ title, children }: React.PropsWithChildren<{ title: string }>) {
  return (
    <div className="card">
      <h2>{title}</h2>
      {children}
    </div>
  );
}

ComponentProps

React.ComponentProps<typeof Component> wyodrębnia propsy istniejącego komponentu, co jest przydatne podczas jego rozszerzania lub opakowywania.

import { Button } from './Button';

type ExtendedButtonProps = React.ComponentProps<typeof Button> & {
  loading?: boolean;
};

function LoadingButton({ loading, children, ...props }: ExtendedButtonProps) {
  return (
    <Button {...props} disabled={loading || props.disabled}>
      {loading ? <Spinner /> : children}
    </Button>
  );
}

ComponentPropsWithoutRef

React.ComponentPropsWithoutRef<'input'> wyodrębnia propsy elementu HTML bez propsa ref — idealnie nadaje się do opakowywania elementów HTML.

type InputProps = React.ComponentPropsWithoutRef<'input'> & {
  label: string;
  error?: string;
};

function FormInput({ label, error, ...inputProps }: InputProps) {
  return (
    <div>
      <label>{label}</label>
      <input {...inputProps} />
      {error && <span className="error">{error}</span>}
    </div>
  );
}

Omit & Pick

Należy użyć Omit<T, Keys>, aby usunąć określone propsy, oraz Pick<T, Keys>, aby zachować tylko wybrane propsy z istniejącego typu.

interface UserCardProps {
  id: string;
  name: string;
  email: string;
  avatar: string;
  role: string;
}

// Only expose non-sensitive fields:
type PublicUserCardProps = Omit<UserCardProps, 'email' | 'role'>;
// Or pick exactly what you need:
type AvatarProps = Pick<UserCardProps, 'name' | 'avatar'>;

Partial & Required

Partial<T> sprawia, że wszystkie propsy stają się opcjonalne, co jest przydatne w formularzach aktualizacji. Required<T> sprawia, że wszystkie propsy stają się wymagane.

interface UserFormData { name: string; email: string; bio: string; }

// For update: only send changed fields
type UserUpdatePayload = Partial<UserFormData>;

// For create: all fields are mandatory
type UserCreatePayload = Required<UserFormData>;

Record dla typowanych słowników

Record<Keys, Value> tworzy typ obiektu z określonymi typami kluczy, co jest przydatne w mapach wyszukiwania i tłumaczeniach.

type Status = 'idle' | 'loading' | 'success' | 'error';

const statusLabels: Record<Status, string> = {
  idle: 'Ready',
  loading: 'Loading...',
  success: 'Done!',
  error: 'Something went wrong',
};

ReturnType dla typów hooków

Należy użyć ReturnType<typeof useMyHook>, aby wywnioskować typ zwracany przez niestandardowy hook bez ręcznego powielania typu.

function useCounter(initial: number) {
  const [count, setCount] = useState(initial);
  return { count, increment: () => setCount(c => c + 1) };
}

type CounterHook = ReturnType<typeof useCounter>;
// { count: number; increment: () => void }

Generyczny komponent tabeli

W pełni typowana tabela danych może działać z dowolnym kształtem wierszy, wywnioskować klucze kolumn i nadal udostępniać autouzupełnianie.

interface Column<T> {
  key: keyof T;
  header: string;
  render?: (value: T[keyof T], row: T) => React.ReactNode;
}

function DataTable<T extends object>({ data, columns }: { data: T[]; columns: Column<T>[] }) {
  return (
    <table>
      <thead><tr>{columns.map(c => <th key={String(c.key)}>{c.header}</th>)}</tr></thead>
      <tbody>{data.map((row, i) => (
        <tr key={i}>{columns.map(c => <td key={String(c.key)}>{c.render ? c.render(row[c.key], row) : String(row[c.key])}</td>)}</tr>
      ))}</tbody>
    </table>
  );
}

Szybkie sprawdzenie

Który typ narzędziowy usuwa wskazane klucze z istniejącego interfejsu TypeScript?

Podsumowanie

Komponenty generyczne używają parametrów typu, aby zachować bezpieczeństwo typów dla różnych kształtów danych. Typy narzędziowe, takie jak Omit, Pick, Partial, ComponentProps i PropsWithChildren, eliminują ręczne powielanie typów i ułatwiają tworzenie kompozycyjnych API komponentów.

Często zadawane pytania

Czy lekcja „Komponenty generyczne i typy narzędziowe” jest bezpłatna?

Tak — pełny tekst „Komponenty generyczne i typy narzędziowe” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu React Academy, przejdź na CoddyKit PRO. Kurs React Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Komponenty generyczne i typy narzędziowe”?

Pisanie wielokrotnego użytku komponentów generycznych oraz używanie PropsWithChildren, ComponentProps i Omit. Ćwiczysz React Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć React Academy?

Nie wymagamy żadnego doświadczenia. React Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Komponenty generyczne i typy narzędziowe”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji React Academy?

Tak. Każda lekcja React Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Typowanie propsów i typów zwracanych przez komponenty
  2. Typowanie zdarzeń i refów w TypeScript
  3. Komponenty generyczne i typy narzędziowe
  4. Typowanie kontekstu i Custom Hooks
← Powrót do React Academy