Paweł Łukasiewicz
2024-12-02
Paweł Łukasiewicz
2024-12-02
Udostępnij Udostępnij Kontakt
Wprowadzenie

TypeScript doskonale współpracuje z React, a jednym z kluczowych elementów tej współpracy jest możliwość precyzyjnego typowania komponentów oraz przekazywanych im danych, czyli propsów. Dzięki temu możemy tworzyć bardziej niezawodne i łatwiejsze w utrzymaniu aplikacje.

W tym wpisie omówimy, jak efektywnie integrować TypeScript z React oraz jak typować komponenty i ich propsy.

Instalacja i konfiguracja TypeScript w projekcie React

Jeśli dopiero zaczynasz pracę z TypeScript w projekcie React, poniżej przedstawiam podstawowy sposób, jak skonfigurować środowisko.

1. Stworzenie nowego projektu React z TypeScript. Możesz stworzyć nową aplikację React z TypeScript przy użyciu create-react-app:

npx create-react-app my-app --template typescript

2. Dodanie TypeScript do istniejącego projektu. Jeśli masz już istniejący projekt React i chcesz dodać do niego TypeScript, wystarczy zainstalować odpowiednie zależności:

npm install typescript @types/react @types/react-dom
Następnie zmień rozszerzenia plików z .js na .tsx dla komponentów, które będą zawierały JSX, oraz .ts dla pozostałych plików TypeScript.

Typowanie komponentów w React

W React możemy tworzyć dwa główne typy komponentów: komponenty funkcyjne i komponenty klasowe. Oba te typy mogą być w pełni typowane przy użyciu TypeScript.

Komponenty funkcyjne

Najpopularniejszym sposobem tworzenia komponentów w React są obecnie komponenty funkcyjne. TypeScript umożliwia typowanie zarówno danych, które komponent przyjmuje, jak i danych, które zwraca.

Przykład komponentu funkcyjnego z typowaniem:

import React from 'react';

// Definicja interfejsu dla propsów
interface WelcomeProps {
  name: string;
  age: number;
}

// Typowany komponent funkcyjny
const Welcome: React.FC<WelcomeProps> = ({ name, age }) => {
  return (
    <div>
        <h1>Witaj, {name}!</h1>
        <p>Masz {age} lat.</p>
    </div>
  );
};

export default Welcome;
W powyższym przykładzie:
  • Zdefiniowaliśmy interfejs WelcomeProps, który określa strukturę propsów (name i age).
  • Komponent funkcyjny Welcome jest typowany jako React.FC<WelcomeProps>, co oznacza, że akceptuje propsy o strukturze zdefiniowanej w interfejsie.

Dzięki temu, TypeScript automatycznie sprawdzi, czy przekazujemy odpowiednie dane do komponentu, i zwróci błąd, jeśli coś będzie niezgodne z typami.

Komponenty klasowe

Mimo że komponenty funkcyjne z hooks stały się standardem, nadal możesz spotkać komponenty klasowe, które również mogą być typowane.

Przykład komponentu klasowego z typowaniem:

import React, { Component } from 'react';

// Interfejs dla propsów
interface GreetingProps {
  message: string;
}

// Typowany komponent klasowy
class Greeting extends Component<GreetingProps> {
  render() {
    return <h1>{this.props.message}</h1>;
  }
}

export default Greeting;
W powyższym przykładzie komponent klasowy Greeting ma typowane propsy zgodnie z interfejsem GreetingProps.

Typowanie propsów

Typowanie propsów w TypeScript pozwala określić, jakie dane komponent może przyjmować. Jest to kluczowy aspekt integracji TypeScript z React, ponieważ pozwala na lepsze zabezpieczenie aplikacji przed błędami związanymi z niepoprawnym przekazywaniem danych.

Przykład bardziej złożonego typowania propsów:

interface ButtonProps {
  label: string;
  onClick: () => void;
  disabled?: boolean; // Opcjonalny props
}

const Button: React.FC<ButtonProps> = ({ label, onClick, disabled }) => {
  return (
    <button onClick={onClick} disabled={disabled}>
      {label}
    </button>
  );
};
  • label: string, który zawsze musi być przekazany.
  • onClick: funkcja, którą trzeba przekazać.
  • disabled: opcjonalny (oznaczony przez ?) props typu boolean, który domyślnie jest wymagany.

Dzięki TypeScriptowi, podczas użycia komponentu będziesz mieć pewność, że przekazywane dane są zgodne z oczekiwaniami.

Typowanie state i ref

Jeżeli pracujesz z klasowymi komponentami lub używasz stanów (state) i referencji (refs) w funkcyjnych komponentach, TypeScript również pozwala na ich precyzyjne typowanie.

Typowanie state w klasowym komponencie:

interface CounterState {
  count: number;
}

class Counter extends React.Component<{}, CounterState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      count: 0
    };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
        <div>
            <p>{this.state.count}</p>
            <button onClick={this.increment}>Dodaj</button>
        </div>
    );
  }
}
W tym przykładzie CounterState definiuje typ stanu, który przechowuje count jako liczbę. Klasa Counter zarządza tym stanem oraz aktualizuje go.

Typowanie useState i useRef w komponentach funkcyjnych:

import React, { useState, useRef } from 'react';

const Counter: React.FC = () => {
  const [count, setCount] = useState<number>(0); // Typowanie useState
  const inputRef = useRef<HTMLInputElement>(null); // Typowanie referencji

  const increment = () => setCount(count + 1);

  return (
    <div>
        <p>{count}</p>
        <button onClick={increment}>Dodaj</button>
        <input ref={inputRef} type="text" />
    </div>
  );
};
Tutaj useState<number> informuje TypeScript, że count będzie liczbą, natomiast useRef<HTMLInputElement> typuje referencję do elementu HTML input.

Typowanie komponentów z children

W React często tworzymy komponenty, które przyjmują inne elementy Reacta jako children. TypeScript pozwala na typowanie takiego przypadku.

Przykład typowania children:

interface CardProps {
  children: React.ReactNode;
}

const Card: React.FC<CardProps> = ({ children }) => {
  return <div className="card">{children}</div>;
};
Typ React.ReactNode oznacza dowolny element React, który może być przekazany jako dziecko komponentu (np. tekst, inne komponenty, elementy HTML).

Typowanie defaultProps

Możemy również typować domyślne wartości propsów w TypeScript, co pozwala zapewnić, że nawet jeśli props nie zostanie przekazany, komponent otrzyma jego domyślną wartość.

Przykład:

interface ButtonProps {
  label: string;
  color?: string;
}

const Button: React.FC<ButtonProps> = ({ label, color = "blue" }) => {
  return <button style={{ backgroundColor: color }}>{label}</button>;
};
W tym przykładzie, nawet jeśli props color nie zostanie przekazany, komponent Button ustawi domyślny kolor na blue.

Podsumowanie

TypeScript w połączeniu z Reactem daje niezwykle potężne narzędzie do tworzenia bezpiecznych i skalowalnych aplikacji. Dzięki typowaniu komponentów i propsów możemy uniknąć wielu błędów już na etapie kompilacji, co znacząco poprawia jakość kodu. Kluczowe elementy, które omówiliśmy, to:

  • Typowanie komponentów funkcyjnych i klasowych.
  • Precyzyjne definiowanie propsów, w tym opcjonalnych.
  • Typowanie stanu (state) i referencji (refs).
  • Praca z komponentami, które mają children.
  • Użycie domyślnych wartości dla propsów.

To tylko wprowadzenie do integracji TypeScript z React. Jak tylko czas pozwoli przygotuje odrębną serię wpisów gdzie ten cykl, pozwoli nam na sprawniejsze (i szybsze) rozpoczęcie pracy z Reactem.