Компонентное дерево
Начнём с высокоуровневого дерева. Каждый компонент имеет чёткую зону ответственности:
App
├── FeedPage
│ ├── FeedHeader // заголовок, фильтры (Лента / Рекомендации)
│ ├── NewPostsBanner // «5 новых постов — показать»
│ ├── FeedList // виртуализированный список
│ │ └── PostCard[] // один пост
│ │ ├── PostHeader // аватар, имя, время
│ │ ├── PostBody // текст + медиа
│ │ ├── PostMedia // фото-карусель или видеоплеер
│ │ ├── PostActions // лайк, коммент, шар, сохранить
│ │ └── PostComments// раскрывающийся блок комментов
│ └── ScrollSentinel // IntersectionObserver для подгрузки
└── CreatePostModal // модалка создания поста
Стратегия рендеринга
Используем гибридный подход: SSR для первых 20 постов (быстрый FCP, SEO для публичных профилей), далее — CSR с подгрузкой через API. Next.js App Router позволяет сделать серверный компонент для начального рендера и клиентский для интерактивной ленты:
// FeedPage — Server Component (первая загрузка)
const initialPosts = await fetchFeed({ limit: 20 });
// FeedList — Client Component (интерактивность)
"use client";
<VirtualizedFeed initialData={initialPosts} />
Управление состоянием
Глобальный store (Zustand или Redux Toolkit) с нормализованной структурой: сущности разделены по типам (posts, users, comments), ссылки по ID. Это позволяет обновлять один пост (лайк) без пересоздания всего списка.
Локальный стейт — UI-состояния: открыта ли модалка, раскрыты ли комментарии, позиция карусели. Эти данные не нужны в глобальном store.
Real-time слой
WebSocket-соединение для получения событий: new_post, update_post, delete_post. Новые посты не вставляются в ленту мгновенно (это дёргает скролл), а копятся в буфере. Пользователь видит баннер «N новых постов» и сам решает, когда их показать.