Polski

Poznaj najlepsze praktyki używania TypeScript z React do tworzenia solidnych, skalowalnych i łatwych w utrzymaniu aplikacji internetowych. Poznaj strukturę projektów, projektowanie komponentów, testowanie i optymalizację.

TypeScript z React: Najlepsze praktyki dla skalowalnych i łatwych w utrzymaniu aplikacji

TypeScript i React to potężne połączenie do tworzenia nowoczesnych aplikacji internetowych. TypeScript wprowadza statyczne typowanie do JavaScript, poprawiając jakość kodu i łatwość jego utrzymania, podczas gdy React zapewnia deklaracyjne podejście oparte na komponentach do budowania interfejsów użytkownika. Ten artykuł omawia najlepsze praktyki używania TypeScript z React do tworzenia solidnych, skalowalnych i łatwych w utrzymaniu aplikacji, dostosowanych do globalnej publiczności.

Dlaczego warto używać TypeScript z React?

Zanim zagłębimy się w najlepsze praktyki, zrozumiejmy, dlaczego TypeScript jest cennym dodatkiem do tworzenia aplikacji React:

Konfiguracja projektu TypeScript React

Używanie Create React App

Najprostszym sposobem na rozpoczęcie nowego projektu TypeScript React jest użycie Create React App z szablonem TypeScript:

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

To polecenie konfiguruje podstawowy projekt React z TypeScript, w tym niezbędne zależności i plik tsconfig.json.

Konfiguracja tsconfig.json

Plik tsconfig.json jest sercem konfiguracji TypeScript. Oto kilka zalecanych ustawień:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

Kluczowe opcje do rozważenia:

Najlepsze praktyki dla komponentów React z TypeScript

Typowanie propsów komponentów

Jednym z najważniejszych aspektów używania TypeScript z React jest prawidłowe typowanie obiektów propsów. Użyj interfejsów lub aliasów typów do zdefiniowania struktury obiektu propsów.

interface MyComponentProps {
  name: string;
  age?: number; // Opcjonalny prop
  onClick: () => void;
}

const MyComponent: React.FC<MyComponentProps> = ({ name, age, onClick }) => {
  return (
    

Hello, {name}!

{age &&

You are {age} years old.

}
); };

Użycie React.FC<MyComponentProps> zapewnia, że komponent jest komponentem funkcyjnym, a propsy są poprawnie typowane.

Typowanie stanu komponentu

Jeśli używasz komponentów klasowych, będziesz również musiał typować stan komponentu. Zdefiniuj interfejs lub alias typu dla obiektu stanu i użyj go w definicji komponentu.

interface MyComponentState {
  count: number;
}

class MyComponent extends React.Component<{}, MyComponentState> {
  state: MyComponentState = {
    count: 0
  };

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

  render() {
    return (
      

Count: {this.state.count}

); } }

W przypadku komponentów funkcyjnych używających hooka useState, TypeScript często potrafi wywnioskować typ zmiennej stanu, ale można go również podać jawnie:

import React, { useState } from 'react';

const MyComponent: React.FC = () => {
  const [count, setCount] = useState<number>(0);

  return (
    

Count: {count}

); };

Używanie Type Guards

Type Guards to funkcje, które zawężają typ zmiennej w określonym zakresie. Są one przydatne podczas pracy z typami złożonymi (union types) lub gdy trzeba upewnić się, że zmienna ma określony typ przed wykonaniem operacji.

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  side: number;
}

type Shape = Circle | Square;

function isCircle(shape: Shape): shape is Circle {
  return shape.kind === "circle";
}

function getArea(shape: Shape): number {
  if (isCircle(shape)) {
    return Math.PI * shape.radius ** 2;
  } else {
    return shape.side ** 2;
  }
}

Funkcja isCircle jest Type Guardem, który sprawdza, czy Shape jest Circle. Wewnątrz bloku if TypeScript wie, że shape jest Circle i pozwala na dostęp do jej właściwości radius.

Obsługa zdarzeń

Podczas obsługi zdarzeń w React z TypeScript ważne jest prawidłowe typowanie obiektu zdarzenia. Użyj odpowiedniego typu zdarzenia z przestrzeni nazw React.

const MyComponent: React.FC = () => {
  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    console.log(event.target.value);
  };

  return (
    <input type="text" onChange={handleChange} />
  );
};

W tym przykładzie React.ChangeEvent<HTMLInputElement> jest używane do typowania obiektu zdarzenia dla zdarzenia zmiany w elemencie wejściowym. Zapewnia to dostęp do właściwości target, która jest HTMLInputElement.

Struktura projektu

Dobrze ustrukturyzowany projekt jest kluczowy dla łatwości utrzymania i skalowalności. Oto sugerowana struktura projektu dla aplikacji TypeScript React:

src/
├── components/
│   ├── MyComponent/
│   │   ├── MyComponent.tsx
│   │   ├── MyComponent.module.css
│   │   └── index.ts
├── pages/
│   ├── HomePage.tsx
│   └── AboutPage.tsx
├── services/
│   ├── api.ts
│   └── auth.ts
├── types/
│   ├── index.ts
│   └── models.ts
├── utils/
│   ├── helpers.ts
│   └── constants.ts
├── App.tsx
├── index.tsx
├── react-app-env.d.ts
└── tsconfig.json

Kluczowe punkty:

Używanie hooków z TypeScript

Hooki React pozwalają na używanie stanu i innych funkcji React w komponentach funkcyjnych. TypeScript współpracuje bezproblemowo z hookami, zapewniając bezpieczeństwo typów i lepsze doświadczenia deweloperskie.

useState

Jak pokazano wcześniej, możesz jawnie typować zmienną stanu podczas używania useState:

import React, { useState } from 'react';

const MyComponent: React.FC = () => {
  const [count, setCount] = useState<number>(0);

  return (
    

Count: {count}

); };

useEffect

Podczas używania useEffect, zwracaj uwagę na tablicę zależności. TypeScript może pomóc w wyłapaniu błędów, jeśli zapomnisz uwzględnić zależność używaną w efekcie.

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

const MyComponent: React.FC = () => {
  const [count, setCount] = useState<number>(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]); // Dodaj 'count' do tablicy zależności

  return (
    

Count: {count}

); };

Jeśli pominięsz count w tablicy zależności, efekt zostanie wykonany tylko raz podczas montowania komponentu, a tytuł dokumentu nie zaktualizuje się, gdy liczba się zmieni. TypeScript ostrzeże Cię o tym potencjalnym problemie.

useContext

Podczas używania useContext musisz podać typ dla wartości kontekstu.

import React, { createContext, useContext } from 'react';

interface ThemeContextType {
  theme: string;
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

const ThemeProvider: React.FC = ({ children }) => {
  // Zaimplementuj logikę motywu tutaj
  return (
     {} }}>
      {children}
    
  );
};

const MyComponent: React.FC = () => {
  const { theme, toggleTheme } = useContext(ThemeContext) as ThemeContextType;

  return (
    

Theme: {theme}

); }; export { ThemeProvider, MyComponent };

Dostarczając typ dla wartości kontekstu, zapewniasz, że hook useContext zwraca wartość o prawidłowym typie.

Testowanie komponentów React z TypeScript

Testowanie jest istotną częścią tworzenia solidnych aplikacji. TypeScript ulepsza testowanie, zapewniając bezpieczeństwo typów i lepsze pokrycie kodu.

Testy jednostkowe

Użyj frameworków do testowania, takich jak Jest i React Testing Library, do testowania jednostkowego swoich komponentów.

// MyComponent.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';

describe('MyComponent', () => {
  it('renders the component with the correct name', () => {
    render(<MyComponent name="John" />);
    expect(screen.getByText('Hello, John!')).toBeInTheDocument();
  });

  it('calls the onClick handler when the button is clicked', () => {
    const onClick = jest.fn();
    render(<MyComponent name="John" onClick={onClick} />);
    fireEvent.click(screen.getByRole('button'));
    expect(onClick).toHaveBeenCalledTimes(1);
  });
});

Sprawdzanie typów w TypeScript pomaga wyłapać błędy w testach, takie jak przekazanie nieprawidłowych propsów lub użycie niewłaściwych obsługiwaczy zdarzeń.

Testy integracyjne

Testy integracyjne weryfikują, czy różne części aplikacji działają razem poprawnie. Użyj narzędzi takich jak Cypress lub Playwright do testów end-to-end.

Optymalizacja wydajności

TypeScript może również pomóc w optymalizacji wydajności, wyłapując potencjalne wąskie gardła wydajności na wczesnym etapie procesu tworzenia.

Memoizacja

Użyj React.memo do memoizacji komponentów funkcyjnych i zapobiegania niepotrzebnym ponownym renderowaniom.

import React from 'react';

interface MyComponentProps {
  name: string;
}

const MyComponent: React.FC<MyComponentProps> = ({ name }) => {
  console.log('Rendering MyComponent');
  return (
    <p>Hello, {name}!</p>
  );
};

export default React.memo(MyComponent);

React.memo ponownie wyrenderuje komponent tylko wtedy, gdy propsy uległy zmianie. Może to znacznie poprawić wydajność, zwłaszcza w przypadku złożonych komponentów.

Dzielenie kodu (Code Splitting)

Użyj dynamicznych importów, aby podzielić kod na mniejsze fragmenty i ładować je na żądanie. Może to skrócić czas początkowego ładowania aplikacji.

import React, { Suspense } from 'react';

const MyComponent = React.lazy(() => import('./MyComponent'));

const App: React.FC = () => {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <MyComponent />
    </Suspense>
  );
};

React.lazy pozwala na dynamiczne importowanie komponentów, które są ładowane tylko wtedy, gdy są potrzebne. Komponent Suspense zapewnia zastępczy interfejs użytkownika podczas ładowania komponentu.

Podsumowanie

Używanie TypeScript z React może znacznie poprawić jakość, łatwość utrzymania i skalowalność Twoich aplikacji internetowych. Postępując zgodnie z tymi najlepszymi praktykami, możesz wykorzystać moc TypeScript do tworzenia solidnych i wydajnych aplikacji, które zaspokajają potrzeby globalnej publiczności. Pamiętaj, aby skupić się na jasnych definicjach typów, dobrze zorganizowanej strukturze projektu i dokładnym testowaniu, aby zapewnić długoterminowy sukces swoich projektów.

Dodatkowe zasoby