Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Struktura projektu React

Dobrze zorganizowana struktura projektu jest kluczowa dla utrzymania kodu, łatwej nawigacji i skalowalności aplikacji React. W tym module przyjrzymy się najlepszym praktykom strukturyzowania projektów React i zrozumiemy, jak organizować pliki i komponenty.

Podstawowa struktura projektu React

Niezależnie od tego, czy używasz Create React App, Vite czy innego narzędzia, podstawowa struktura projektu React zazwyczaj wygląda podobnie:

1moj-projekt-react/
2├── node_modules/        # Zainstalowane zależności
3├── public/              # Pliki statyczne dostępne publicznie
4├── src/                 # Kod źródłowy aplikacji
5│   ├── components/      # Komponenty wielokrotnego użytku
6│   ├── pages/           # Komponenty reprezentujące całe strony
7│   ├── assets/          # Zasoby takie jak obrazy, fonty itp.
8│   ├── utils/           # Funkcje pomocnicze
9│   ├── services/        # Kod odpowiedzialny za komunikację API
10│   ├── hooks/           # Niestandardowe hooki React
11│   ├── context/         # Konteksty React
12│   ├── App.js           # Główny komponent aplikacji
13│   └── index.js         # Punkt wejściowy aplikacji
14├── .gitignore           # Pliki ignorowane przez Git
15├── package.json         # Informacje o projekcie i zależności
16└── README.md            # Dokumentacja projektu

Omówmy najważniejsze katalogi i ich przeznaczenie:

Katalog src/

Katalog

src/
to serce Twojej aplikacji React. Zawiera całą logikę, komponenty i zasoby. Oto jak zazwyczaj organizuje się ten katalog:

components/

Zawiera komponenty wielokrotnego użytku, które są używane w różnych częściach aplikacji. Istnieje kilka podejść do strukturyzowania komponentów:

Struktura płaska

1components/
2├── Button.jsx
3├── Button.css
4├── Header.jsx
5├── Header.css
6├── Footer.jsx
7└── Footer.css

Struktura pogrupowana (preferowana dla większych projektów)

1components/
2├── Button/
3│   ├── Button.jsx
4│   ├── Button.css
5│   └── index.js
6├── Header/
7│   ├── Header.jsx
8│   ├── Header.css
9│   └── index.js
10└── Footer/
11    ├── Footer.jsx
12    ├── Footer.css
13    └── index.js

Używanie plików

index.js
do eksportowania komponentów upraszcza importy:

1// Zamiast:
2import Button from './components/Button/Button';
3
4// Możesz pisać:
5import Button from './components/Button';

pages/

Komponenty reprezentujące całe strony lub widoki w aplikacji. W projektach z routingiem, każdy plik w tym katalogu zazwyczaj odpowiada określonej ścieżce URL:

1pages/
2├── Home/
3│   ├── Home.jsx
4│   └── Home.css
5├── About/
6│   ├── About.jsx
7│   └── About.css
8└── Dashboard/
9    ├── Dashboard.jsx
10    └── Dashboard.css

assets/

Zawiera wszystkie statyczne zasoby używane w aplikacji:

1assets/
2├── images/
3│   ├── logo.png
4│   └── background.jpg
5├── fonts/
6│   ├── roboto.ttf
7│   └── open-sans.ttf
8└── styles/
9    ├── variables.css
10    └── global.css

utils/

Funkcje pomocnicze i narzędzia używane w całej aplikacji:

1utils/
2├── formatDate.js
3├── validation.js
4└── helpers.js

services/

Kod odpowiedzialny za komunikację z zewnętrznymi API lub usługami:

1services/
2├── api.js           # Podstawowa konfiguracja API
3├── userService.js   # Zapytania związane z użytkownikami
4└── productService.js # Zapytania związane z produktami

hooks/

Niestandardowe hooki React, które można używać w wielu miejscach aplikacji:

1hooks/
2├── useLocalStorage.js
3├── useFetch.js
4└── useForm.js

context/

Konteksty React używane do zarządzania globalnym stanem:

1context/
2├── AuthContext.js
3├── ThemeContext.js
4└── CartContext.js

Organizacja według funkcji vs. według typu

Istnieją dwa główne podejścia do organizacji kodu:

Organizacja według typu (pokazana powyżej)

Pliki są grupowane według ich typu (komponenty, hooki, usługi itp.).

Organizacja według funkcji

Pliki są grupowane według funkcji lub części aplikacji:

1src/
2├── auth/
3│   ├── components/
4│   ├── hooks/
5│   ├── services/
6│   └── context/
7├── products/
8│   ├── components/
9│   ├── hooks/
10│   ├── services/
11│   └── utils/
12└── checkout/
13    ├── components/
14    ├── hooks/
15    └── services/

Ta struktura jest bardziej skalowalna dla dużych projektów, szczególnie gdy pracuje nad nimi wiele osób.

Konwencje nazewnictwa

Spójne nazewnictwo jest równie ważne jak struktura katalogów:

Komponenty

  • Używaj PascalCase:
    Button.jsx
    ,
    UserProfile.jsx
  • Pliki JSX powinny mieć rozszerzenie
    .jsx
    (nie jest to konieczne, ale pomaga w identyfikacji)

Hooki

  • Zawsze zaczynaj od "use":
    useLocalStorage.js
    ,
    useWindowSize.js

Konteksty

  • Zazwyczaj kończą się na "Context":
    AuthContext.js
    ,
    ThemeContext.js

Usługi i narzędzia

  • Używaj camelCase:
    apiService.js
    ,
    formatDate.js

Styles w React

Istnieje kilka podejść do organizacji stylów w React:

CSS Modules

1Button/
2├── Button.jsx
3└── Button.module.css

Styled Components

1Button/
2└── Button.jsx  # Zawiera definicje styled-components

SCSS/SASS

1Button/
2├── Button.jsx
3└── Button.scss

Best Practices struktury folderów

W kosmicznej flocie React, dobrze zorganizowana struktura folderów to jak efektywny system logistyczny na statku kosmicznym - wszystko ma swoje miejsce i można szybko znaleźć to, czego potrzebujemy. Oto sprawdzone praktyki organizacji projektów React.

Feature-based vs Type-based organization

Dwa główne podejścia do organizacji kodu w projektach React:

Type-based organization (organizacja według typu)

Pliki grupowane według ich roli technicznej - wszystkie komponenty razem, wszystkie hooki razem, etc.

1src/
2├── components/
3│   ├── Button/
4│   ├── Card/
5│   ├── Modal/
6│   ├── UserProfile/
7│   ├── ProductCard/
8│   └── ShoppingCart/
9├── hooks/
10│   ├── useAuth.js
11│   ├── useProducts.js
12│   └── useCart.js
13├── services/
14│   ├── authService.js
15│   ├── productService.js
16│   └── cartService.js
17└── utils/
18    ├── validators.js
19    └── formatters.js

Zalety:

  • Prostsza dla małych i średnich projektów
  • Łatwa do zrozumienia dla nowych programistów
  • Dobra dla aplikacji SPA bez wyraźnych modułów

Wady:

  • W dużych projektach trudno znaleźć wszystkie pliki związane z jedną funkcjonalnością
  • Komponenty z różnych funkcjonalności są wymieszane
  • Trudniejsze do skalowania w zespołach

Feature-based organization (organizacja według funkcji)

Pliki grupowane według funkcjonalności biznesowej - wszystkie pliki dotyczące autentykacji razem, wszystkie dotyczące produktów razem, etc.

1src/
2├── features/
3│   ├── auth/
4│   │   ├── components/
5│   │   │   ├── LoginForm/
6│   │   │   ├── RegisterForm/
7│   │   │   └── PasswordReset/
8│   │   ├── hooks/
9│   │   │   ├── useAuth.js
10│   │   │   └── useSession.js
11│   │   ├── services/
12│   │   │   └── authService.js
13│   │   ├── utils/
14│   │   │   └── validators.js
15│   │   └── index.js
16│   │
17│   ├── products/
18│   │   ├── components/
19│   │   │   ├── ProductList/
20│   │   │   ├── ProductCard/
21│   │   │   └── ProductDetails/
22│   │   ├── hooks/
23│   │   │   ├── useProducts.js
24│   │   │   └── useProductFilters.js
25│   │   ├── services/
26│   │   │   └── productService.js
27│   │   └── index.js
28│   │
29│   └── cart/
30│       ├── components/
31│       │   ├── ShoppingCart/
32│       │   ├── CartItem/
33│       │   └── Checkout/
34│       ├── hooks/
35│       │   └── useCart.js
36│       ├── services/
37│       │   └── cartService.js
38│       └── index.js
3940├── shared/
41│   ├── components/
42│   │   ├── Button/
43│   │   ├── Modal/
44│   │   └── Card/
45│   ├── hooks/
46│   │   ├── useLocalStorage.js
47│   │   └── useDebounce.js
48│   └── utils/
49│       ├── formatDate.js
50│       └── formatCurrency.js
5152└── App.jsx

Zalety:

  • Łatwiejsze skalowanie dla dużych projektów
  • Wszystkie pliki związane z funkcją są w jednym miejscu
  • Łatwiejsze do pracy w zespołach (różne zespoły mogą pracować nad różnymi features)
  • Łatwiejsze do usuwania całych funkcjonalności
  • Lepsze dla code splitting

Wady:

  • Bardziej skomplikowana dla początkujących
  • Wymaga więcej przemyślenia przy projektowaniu
  • Może prowadzić do duplikacji dla bardzo małych komponentów

Naming conventions - konwencje nazewnictwa

Spójne nazewnictwo to kluczowy element dobrze zorganizowanego projektu. W React obowiązują następujące konwencje:

Komponenty - PascalCase

1// DOBRZE - PascalCase
2Button.jsx
3UserProfile.jsx
4ShoppingCart.jsx
5ProductCard.jsx
6
7// ZLE - inne konwencje
8button.jsx
9user-profile.jsx
10shopping_cart.jsx

Dlaczego PascalCase?

  • Odróżnia komponenty React od zwykłych funkcji
  • Konwencja przyjęta w całym ekosystemie React
  • JSX wymaga PascalCase dla komponentów (niestandardowe tagi)

Hooki - camelCase z przedrostkiem "use"

1// DOBRZE - camelCase z "use"
2useAuth.js
3useLocalStorage.js
4useWindowSize.js
5useProductFilters.js
6
7// ZLE - brak "use" lub zła konwencja
8Auth.js
9local-storage.js
10WindowSize.js

Dlaczego "use" prefix?

  • React wymaga aby niestandardowe hooki zaczynały się od "use"
  • Pozwala React na walidację reguł hooków
  • Natychmiast komunikuje, że to hook

Konteksty - PascalCase z sufiksem "Context"

1// DOBRZE - PascalCase z "Context"
2AuthContext.js
3ThemeContext.js
4CartContext.js
5UserPreferencesContext.js
6
7// ZLE - brak "Context" lub zła konwencja
8auth.js
9theme-context.js
10Cart.js

Utils i services - camelCase

1// DOBRZE - camelCase
2formatDate.js
3validateEmail.js
4apiService.js
5authService.js
6
7// ZLE - inne konwencje
8FormatDate.js
9validate-email.js
10API_Service.js

Gdzie trzymać różne typy plików

Rozplanowanie lokalizacji plików w projekcie ma ogromne znaczenie dla jego czytelności i skalowalności.

Hooks (niestandardowe hooki)

Feature-based approach:

1features/
2├── auth/
3│   └── hooks/
4│       ├── useAuth.js
5│       └── useSession.js
6└── products/
7    └── hooks/
8        └── useProducts.js
9
10shared/
11└── hooks/
12    ├── useLocalStorage.js  # Współdzielony hook
13    ├── useDebounce.js      # Współdzielony hook
14    └── useFetch.js         # Współdzielony hook

Type-based approach:

1hooks/
2├── useAuth.js
3├── useSession.js
4├── useProducts.js
5├── useLocalStorage.js
6├── useDebounce.js
7└── useFetch.js

Utils (funkcje pomocnicze)

Feature-based approach:

1features/
2├── products/
3│   └── utils/
4│       ├── formatPrice.js
5│       └── filterProducts.js
6└── auth/
7    └── utils/
8        └── validatePassword.js
9
10shared/
11└── utils/
12    ├── formatDate.js       # Współdzielone
13    ├── debounce.js         # Współdzielone
14    └── cloneDeep.js        # Współdzielone

Type-based approach:

1utils/
2├── formatPrice.js
3├── formatDate.js
4├── validatePassword.js
5├── filterProducts.js
6├── debounce.js
7└── cloneDeep.js

Types (TypeScript)

Jeśli używasz TypeScript, masz kilka opcji dla typów:

Opcja 1: Obok plików, które używają:

1components/
2└── Button/
3    ├── Button.tsx
4    ├── Button.types.ts     # Typy tylko dla Button
5    ├── Button.styles.ts
6    └── index.ts

Opcja 2: Scentralizowane typy:

1types/
2├── user.types.ts
3├── product.types.ts
4├── cart.types.ts
5└── api.types.ts

Opcja 3: Hybrydowe (najlepsze):

1features/
2└── products/
3    ├── components/
4    ├── types/              # Typy specyficzne dla feature
5    │   ├── product.types.ts
6    │   └── filter.types.ts
7    └── index.ts
8
9shared/
10└── types/                  # Typy współdzielone
11    ├── common.types.ts
12    └── api.types.ts

Przykładowa struktura projektu - najlepsze praktyki

Połączenie feature-based dla głównych funkcjonalności i type-based dla współdzielonych zasobów:

1moja-aplikacja-react/
2├── public/
3├── src/
4│   ├── features/                    # Główne funkcjonalności
5│   │   ├── auth/
6│   │   │   ├── components/
7│   │   │   │   ├── LoginForm/
8│   │   │   │   │   ├── LoginForm.tsx
9│   │   │   │   │   ├── LoginForm.styles.ts
10│   │   │   │   │   ├── LoginForm.test.tsx
11│   │   │   │   │   └── index.ts
12│   │   │   │   └── RegisterForm/
13│   │   │   ├── hooks/
14│   │   │   │   ├── useAuth.ts
15│   │   │   │   └── useSession.ts
16│   │   │   ├── services/
17│   │   │   │   └── authService.ts
18│   │   │   ├── types/
19│   │   │   │   └── auth.types.ts
20│   │   │   └── index.ts
21│   │   │
22│   │   ├── products/
23│   │   │   ├── components/
24│   │   │   │   ├── ProductList/
25│   │   │   │   ├── ProductCard/
26│   │   │   │   └── ProductFilters/
27│   │   │   ├── hooks/
28│   │   │   │   ├── useProducts.ts
29│   │   │   │   └── useProductFilters.ts
30│   │   │   ├── services/
31│   │   │   │   └── productService.ts
32│   │   │   ├── types/
33│   │   │   │   └── product.types.ts
34│   │   │   └── index.ts
35│   │   │
36│   │   └── cart/
37│   │       └── [podobna struktura]
38│   │
39│   ├── shared/                      # Współdzielone zasoby
40│   │   ├── components/
41│   │   │   ├── Button/
42│   │   │   │   ├── Button.tsx
43│   │   │   │   ├── Button.styles.ts
44│   │   │   │   ├── Button.test.tsx
45│   │   │   │   └── index.ts
46│   │   │   ├── Modal/
47│   │   │   ├── Card/
48│   │   │   └── Layout/
49│   │   │
50│   │   ├── hooks/
51│   │   │   ├── useLocalStorage.ts
52│   │   │   ├── useDebounce.ts
53│   │   │   └── useFetch.ts
54│   │   │
55│   │   ├── utils/
56│   │   │   ├── formatDate.ts
57│   │   │   ├── validators.ts
58│   │   │   └── helpers.ts
59│   │   │
60│   │   ├── types/
61│   │   │   ├── common.types.ts
62│   │   │   └── api.types.ts
63│   │   │
64│   │   └── constants/
65│   │       ├── routes.ts
66│   │       └── config.ts
67│   │
68│   ├── layouts/                     # Layout komponenty
69│   │   ├── MainLayout/
70│   │   ├── AuthLayout/
71│   │   └── DashboardLayout/
72│   │
73│   ├── pages/                       # Komponenty stron (dla routingu)
74│   │   ├── HomePage/
75│   │   ├── ProductsPage/
76│   │   ├── ProductDetailPage/
77│   │   └── CartPage/
78│   │
79│   ├── context/                     # Globalne konteksty
80│   │   ├── AuthContext.tsx
81│   │   ├── ThemeContext.tsx
82│   │   └── CartContext.tsx
83│   │
84│   ├── router/                      # Konfiguracja routingu
85│   │   ├── routes.tsx
86│   │   └── ProtectedRoute.tsx
87│   │
88│   ├── styles/                      # Globalne style
89│   │   ├── globals.css
90│   │   ├── variables.css
91│   │   └── reset.css
92│   │
93│   ├── App.tsx
94│   └── index.tsx
9596├── .eslintrc.js
97├── .prettierrc
98├── tsconfig.json
99├── package.json
100└── README.md

Zasady organizacji plików

  1. Reguła 3 poziomów maksymalnie
    • Unikaj głębszego zagnieżdżenia niż 3 poziomy
    • Jeśli potrzebujesz więcej, prawdopodobnie twoja struktura jest za skomplikowana
1// DOBRZE - 3 poziomy
2src/features/products/components/
3
4// ZLE - za głębokie
5src/modules/ecommerce/features/products/ui/components/
  1. Eksportuj przez index.ts
    • Każdy folder z komponentem ma index.ts
    • Upraszcza importy i ukrywa szczegóły implementacji
1// components/Button/index.ts
2export { default } from './Button';
3export * from './Button.types';
4
5// Użycie
6import Button from './components/Button';  // czytelne
7// zamiast
8import Button from './components/Button/Button';  // rozwlekłe
  1. Colocation - trzymaj blisko to, co jest używane razem
    • Style obok komponentu
    • Testy obok komponentu
    • Typy obok komponentu
1Button/
2├── Button.tsx          # Komponent
3├── Button.styles.ts    # Style
4├── Button.test.tsx     # Testy
5├── Button.types.ts     # Typy
6└── index.ts            # Eksport
  1. Shared vs Feature-specific
    • Jeśli coś jest używane w więcej niż 2 miejscach → shared
    • Jeśli coś jest specyficzne dla feature → trzymaj w feature

Praktyczne wskazówki dotyczące struktury projektu

  1. Zacznij prosto - Nie przesadzaj ze skomplikowaną strukturą na początku projektu. Dla małej aplikacji type-based może wystarczyć, dopiero później refaktoryzuj do feature-based gdy projekt urośnie.

  2. Refaktoryzuj, gdy zajdzie potrzeba - Dostosowuj strukturę w miarę rozwoju projektu. Jeśli folder ma więcej niż 10-15 plików, rozważ jego podział.

  3. Utrzymuj małe komponenty - Komponenty powinny być małe i skupione na jednym zadaniu. Jeśli komponent ma więcej niż 200-300 linii, rozważ jego podział.

  4. Grupuj powiązane pliki - Trzymaj razem pliki, które są ze sobą ściśle powiązane. Jeśli zawsze edytujesz dwa pliki razem, prawdopodobnie powinny być w tym samym folderze.

  5. Dokumentuj - Dodaj README.md do głównych katalogów, aby wyjaśnić ich przeznaczenie i strukturę.

  6. Konsystencja przede wszystkim - Wybierz jeden styl (feature-based lub type-based) i trzymaj się go w całym projekcie. Najgorsze jest mieszanie stylów.

Narzędzia pomagające w organizacji kodu

  1. ESLint - Pomaga utrzymać spójny styl kodu i wyłapać potencjalne błędy
  2. Prettier - Automatycznie formatuje kod według ustalonych reguł
  3. Import sorter - Sortuje importy dla lepszej czytelności
  4. Path aliases - Upraszcza importy używając aliasów jak @components, @utils
1// tsconfig.json lub jsconfig.json
2{
3  "compilerOptions": {
4    "baseUrl": "src",
5    "paths": {
6      "@components/*": ["shared/components/*"],
7      "@features/*": ["features/*"],
8      "@utils/*": ["shared/utils/*"],
9      "@hooks/*": ["shared/hooks/*"]
10    }
11  }
12}
13
14// Użycie
15import Button from '@components/Button';
16import { useAuth } from '@features/auth';
17import { formatDate } from '@utils/formatDate';

Dobrze zorganizowana struktura projektu znacznie ułatwia pracę, szczególnie w dużych zespołach i projektach. Pamiętaj, że nie ma jedynej "właściwej" struktury - najważniejsze, aby była spójna, logiczna dla Twojego zespołu i skalowalna wraz z rozwojem projektu.

Ir a CodeWorlds