Главная/React и Next.js/Блупринты/Стейт & Real-time/Real-time Kanban-доска с WebSockets / SSE и локальным стейтом Zustand
К карточкам раздела «Стейт & Real-time»
Стейт & Real-time(ТЗ 5 из 10)
ТЗ: Real-time Kanban-доска с WebSockets / SSE и локальным стейтом Zustand
Стейт & Real-time1 шагов • +5 XP

ТЗ: Real-time Kanban-доска с WebSockets / SSE и локальным стейтом Zustand

Архитектурный блупринт разработки интерактивной доски задач: плавный Drag-and-Drop, локальный стейт Zustand с оптимистичным откликом, стриминг изменений коллег через SSE и разрешение конфликтов версий.

~14 ч работыZustand v5Server-Sent Events (SSE)@hello-pangea/dnd / dnd-kitOptimistic MutationsVector Clocks / VersioningTypeScript
+5 XP за шаг
Выполнено 0 из 1 шагов (0%)
Трудозатраты~14 рабочих часов
Фриланс вилка~$630 – $1,050 (€560 – €980)
Оценка веб-студии~$1,330 – $2,240 (€1260 – €2100)

ТЗ заказчика (исходный бриф):

Главное ощущение — скорость и плавность. Если перетаскиваешь карточку, она не должна возвращаться назад из-за задержки сервера. Если двое двигают одну карточку, должен побеждать последний автор без потери текста.
Ключевые требования:
  • •Zustand хранилище колонок и карточек с иммутабельными обновлениями
  • •Слушатель Server-Sent Events (SSE) для мгновенного получения мутаций от коллег
  • •Оптимистичное перемещение карточки с откатом при сбое сети
  • •Поддержка драг-н-дропа с клавиатуры для доступности
Ограничения проекта:
  • •Не вызывать каскадные ререндеры всей доски при перемещении одной карточки
  • •Автоматический реконнект SSE при потере интернет-соединения
Критерии приёмки (KPI):
  • Frame rate при drag-and-drop строго 60 FPS
  • Задержка доставки чужого события < 100ms

Пошаговое практическое руководство по ТЗ

Подробный разбор архитектуры, код реализации и советы эксперта по каждому этапу:

1
Этап 1 из 1

Zustand хранилище с атомарными селекторами и оптимистичным DnD

~60 минСложный

Практическое руководство:

Создать store/kanbanStore.ts с операциями moveTask, addTask, updateTaskFromRemote. Использовать immer или нативные спрэды без мутаций.

Важные технические нюансы и подводные камни:

Используйте селекторы вида useKanbanStore(s => s.columns[colId]) для предотвращения холостых ререндеров всей доски.

Архитектурная база (Почему выбрано такое решение):

Нормализованная структура (Record<string, Task>) гарантирует константную сложность O(1) при обновлении и предотвращает ререндер соседних карточек.

Совет эксперта (Best Practice):

Подписывайте компоненты колонок только на массив ID своих карточек, а не на объекты задач целиком.

Ожидаемый артефакт этапа:

Файл kanbanStore.ts с оптимистичными мутациями и разрешением версионных конфликтов.

TYPESCRIPT
// store/kanbanStore.ts
import { create } from 'zustand'

export interface Task {
  id: string
  title: string
  columnId: string
  version: number
}

interface KanbanState {
  tasks: Record<string, Task>
  moveTask: (taskId: string, targetColId: string) => void
  applyRemoteUpdate: (task: Task) => void
}

export const useKanbanStore = create<KanbanState>((set) => ({
  tasks: {},

  moveTask: (taskId, targetColId) => {
    set((state) => {
      const task = state.tasks[taskId]
      if (!task || task.columnId === targetColId) return state

      return {
        tasks: {
          ...state.tasks,
          [taskId]: {
            ...task,
            columnId: targetColId,
            version: task.version + 1
          }
        }
      }
    })
  },

  applyRemoteUpdate: (remoteTask) => {
    set((state) => {
      const localTask = state.tasks[remoteTask.id]
      // Разрешение конфликтов: принимаем обновление, если версия новее
      if (!localTask || remoteTask.version >= localTask.version) {
        return {
          tasks: { ...state.tasks, [remoteTask.id]: remoteTask }
        }
      }
      return state
    })
  }
}))

Типичные грабли на этом ТЗ

1 антипаттерна

Что сдаём клиенту (Deliverables)

Чек-лист сдачи
Нормализованное хранилище Zustand v5
Плавный Drag-and-Drop без лагов
SSE клиент с авто-переподключением и синхронизацией