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:
- Poprawiona jakość kodu: Statyczne typowanie TypeScript pomaga wyłapywać błędy na wczesnym etapie procesu tworzenia, redukując problemy w czasie wykonywania i poprawiając niezawodność kodu.
- Zwiększona łatwość utrzymania: Adnotacje typów i interfejsy ułatwiają zrozumienie i refaktoryzację kodu, prowadząc do lepszej długoterminowej łatwości utrzymania.
- Lepsze wsparcie IDE: TypeScript zapewnia doskonałe wsparcie IDE, w tym autouzupełnianie, nawigację po kodzie i narzędzia do refaktoryzacji, zwiększając produktywność programistów.
- Zredukowana liczba błędów: Statyczne typowanie wyłapuje wiele powszechnych błędów JavaScript przed uruchomieniem, co prowadzi do bardziej stabilnej i wolnej od błędów aplikacji.
- Lepsza współpraca: Jasne definicje typów ułatwiają zespołom współpracę nad dużymi projektami, ponieważ programiści mogą szybko zrozumieć przeznaczenie i użycie różnych komponentów i funkcji.
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:
"strict": true
: Włącza ścisłe sprawdzanie typów, co jest wysoce zalecane do wyłapywania potencjalnych błędów."esModuleInterop": true
: Włącza interoperacyjność między modułami CommonJS i ES."jsx": "react-jsx"
: Włącza nowy transformator JSX, który upraszcza kod React i poprawia wydajność.
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:
- Komponenty: Grupuj powiązane komponenty w katalogach. Każdy katalog powinien zawierać plik TypeScript komponentu, moduły CSS (jeśli są używane) i plik
index.ts
do eksportowania komponentu. - Strony: Przechowuj komponenty najwyższego poziomu, które reprezentują różne strony aplikacji.
- Usługi: Implementuj wywołania API i inne usługi w tym katalogu.
- Typy: Definiuj globalne definicje typów i interfejsy w tym katalogu.
- Narzędzia (Utils): Przechowuj funkcje pomocnicze i stałe.
- index.ts: Użyj plików
index.ts
do ponownego eksportowania modułów z katalogu, zapewniając czyste i zorganizowane API do importowania modułów.
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.