← Назад к вопросам
Как понять что HTTP запрос успешный?
1.8 Middle🔥 141 комментариев
#Браузер и сетевые технологии
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Организация файловой структуры при добавлении нового функционала
При разработке нового функционала в React приложении очень важна правильная организация файлов. Это упрощает масштабирование, тестирование и поддержку кода. Рассмотрю пример добавления функционала "Система комментариев".
Рекомендуемая структура проекта
src/
├── components/
│ ├── ui/ # Переиспользуемые UI компоненты
│ │ ├── Button.tsx
│ │ ├── Card.tsx
│ │ ├── Input.tsx
│ │ └── Modal.tsx
│ │
│ ├── comments/ # Компоненты для комментариев
│ │ ├── CommentList.tsx # Список комментариев
│ │ ├── CommentItem.tsx # Один комментарий
│ │ ├── CommentForm.tsx # Форма добавления
│ │ ├── CommentThread.tsx # Ветка комментариев
│ │ └── index.ts # Экспорты компонентов
│ │
│ └── layout/
│ ├── Header.tsx
│ └── Footer.tsx
│
├── hooks/
│ ├── comments/ # Хуки для комментариев
│ │ ├── useComments.ts # Получение комментариев
│ │ ├── useCreateComment.ts # Создание комментария
│ │ ├── useDeleteComment.ts # Удаление комментария
│ │ └── index.ts
│ │
│ └── useAuth.ts
│
├── contexts/
│ ├── CommentsContext.tsx # Context для комментариев
│ └── AuthContext.tsx
│
├── services/
│ ├── comments/ # Бизнес-логика для комментариев
│ │ ├── commentService.ts # API методы
│ │ ├── commentValidator.ts # Валидация
│ │ └── index.ts
│ │
│ └── authService.ts
│
├── types/
│ ├── comment.ts # Типы для комментариев
│ ├── user.ts
│ └── api.ts
│
├── lib/
│ ├── api.ts # HTTP клиент
│ ├── utils.ts # Утилиты
│ └── constants.ts
│
├── pages/
│ └── [id]/
│ └── page.tsx
│
└── tests/
└── comments/
├── CommentList.test.tsx
├── useComments.test.ts
├── commentService.test.ts
└── __mocks__/
└── comments.ts
Пример реализации: Система комментариев
1. Типы (types/comment.ts)
export interface Comment {
id: string;
postId: string;
authorId: string;
content: string;
createdAt: Date;
updatedAt: Date;
likes: number;
replies?: Comment[];
}
export interface CreateCommentDTO {
content: string;
postId: string;
parentCommentId?: string;
}
export interface CommentError {
code: string;
message: string;
}
2. Сервис (services/comments/commentService.ts)
import { api } from '@/lib/api';
import type { Comment, CreateCommentDTO } from '@/types/comment';
export class CommentService {
private readonly baseUrl = '/api/v1/comments';
async getComments(postId: string): Promise<Comment[]> {
const response = await api.get(`${this.baseUrl}`, {
params: { postId }
});
return response.data;
}
async createComment(data: CreateCommentDTO): Promise<Comment> {
const response = await api.post(this.baseUrl, data);
return response.data;
}
async updateComment(id: string, content: string): Promise<Comment> {
const response = await api.patch(`${this.baseUrl}/${id}`, { content });
return response.data;
}
async deleteComment(id: string): Promise<void> {
await api.delete(`${this.baseUrl}/${id}`);
}
async likeComment(id: string): Promise<Comment> {
const response = await api.post(`${this.baseUrl}/${id}/like`);
return response.data;
}
}
export const commentService = new CommentService();
3. Custom Hook (hooks/comments/useComments.ts)
import { useState, useCallback } from 'react';
import { commentService } from '@/services/comments';
import type { Comment, CreateCommentDTO } from '@/types/comment';
export function useComments(postId: string) {
const [comments, setComments] = useState<Comment[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchComments = useCallback(async () => {
setLoading(true);
setError(null);
try {
const data = await commentService.getComments(postId);
setComments(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Неизвестная ошибка');
} finally {
setLoading(false);
}
}, [postId]);
const addComment = useCallback(async (content: string) => {
try {
const newComment = await commentService.createComment({
content,
postId
});
setComments((prev) => [newComment, ...prev]);
return newComment;
} catch (err) {
setError(err instanceof Error ? err.message : 'Ошибка при создании');
throw err;
}
}, [postId]);
const removeComment = useCallback(async (commentId: string) => {
try {
await commentService.deleteComment(commentId);
setComments((prev) => prev.filter((c) => c.id !== commentId));
} catch (err) {
setError(err instanceof Error ? err.message : 'Ошибка при удалении');
throw err;
}
}, []);
return {
comments,
loading,
error,
fetchComments,
addComment,
removeComment
};
}
4. Компонент (components/comments/CommentList.tsx)
import { useEffect } from 'react';
import { useComments } from '@/hooks/comments';
import { CommentForm } from './CommentForm';
import { CommentItem } from './CommentItem';
interface CommentListProps {
postId: string;
}
export function CommentList({ postId }: CommentListProps) {
const { comments, loading, error, fetchComments, addComment, removeComment } =
useComments(postId);
useEffect(() => {
fetchComments();
}, [fetchComments]);
return (
<div className="space-y-4">
<CommentForm onSubmit={addComment} />
{loading && <p>Загрузка...</p>}
{error && <p className="text-red-600">{error}</p>}
<div className="space-y-3">
{comments.map((comment) => (
<CommentItem
key={comment.id}
comment={comment}
onDelete={removeComment}
/>
))}
</div>
</div>
);
}
Ключевые принципы организации
- Разделение ответственности — каждая папка отвечает за одну область функционала
- Иерархия слоёв — types > services > hooks > components
- Переиспользуемость — UI компоненты в
ui/, специфичные в[feature]/ - Тестируемость — рядом с компонентами лежат тесты
- Масштабируемость — легко добавлять новый функционал без изменения существующей структуры
Эта структура обеспечивает чистоту кода и упрощает командную разработку.