Структура React-приложения
Хорошая структура проекта упрощает навигацию и поддержку. Универсального правила нет, но есть проверенные подходы.
Базовая структура
src/
├── components/ # переиспользуемые компоненты
├── pages/ # страницы (если есть роутинг)
├── hooks/ # кастомные хуки
├── utils/ # вспомогательные функции
├── api/ # запросы к серверу
├── types/ # общие типы
├── App.tsx
└── main.tsx
По типам vs по фичам
По типам — файлы группируются по техническому назначению:
components/
Button.tsx
Card.tsx
Modal.tsx
hooks/
useAuth.ts
useFetch.ts
Плюс: понятно, что где лежит. Минус: при росте проекта компоненты одной фичи разбросаны.
По фичам — группировка по функциональности:
features/
auth/
components/
LoginForm.tsx
hooks/
useAuth.ts
api/
authApi.ts
types.ts
posts/
components/
PostCard.tsx
hooks/
usePosts.ts
api/
postsApi.ts
types.ts
Плюс: всё по фиче в одном месте. Минус: дублирование папок.
Компоненты
Именование: PascalCase.tsx.
components/
Button/
Button.tsx
Button.module.css
index.ts
Card/
Card.tsx
index.ts
Или плоско:
components/
Button.tsx
Button.module.css
Card.tsx
Card.module.css
Выбор зависит от размера компонента. Если у него много файлов — папка. Если один — плоско.
Хуки
Именование: useSomething.ts.
// hooks/useLocalStorage.ts
export function useLocalStorage<T>(key: string, initial: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initial;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const;
}
API-слой
Все запросы к серверу — в одном месте:
// api/posts.ts
const API_URL = import.meta.env.VITE_API_URL;
export async function getPosts(): Promise<Post[]> {
const res = await fetch(`${API_URL}/posts`);
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}
export async function createPost(data: PostInput): Promise<Post> {
const res = await fetch(`${API_URL}/posts`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('Failed to create');
return res.json();
}
Типы
Общие типы — в types/:
export type User = {
id: number;
name: string;
email: string;
};
export type Post = {
id: number;
title: string;
body: string;
author: User;
};
Локальные типы компонента — рядом с компонентом.
Утилиты
// utils/format.ts
export function formatDate(date: Date): string {
return new Intl.DateTimeFormat('ru-RU').format(date);
}
Алиасы путей
Чтобы не писать ../../components/Button, настрой алиасы в tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
И в vite.config.ts:
import path from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Теперь импорт:
import { Button } from '@/components/Button';
Правила
- Не держи всё в одной папке
- Держи связанные файлы рядом
- Не создавай глубокую вложенность без причины
- Переиспользуй компоненты, а не копируй
- Логику выноси в хуки
Итоги
- Структура зависит от размера проекта
- Для маленьких — по типам, для больших — по фичам
- Компоненты, хуки, API, типы, утилиты
- Алиасы путей упрощают импорты
- Связанные файлы держи рядом