Archon/PRPs/ai_docs/optimistic_updates.md
Wirasm 63a92cf7d7
Some checks failed
Continuous Integration / Frontend Tests (React + Vitest) (push) Has been cancelled
Continuous Integration / Backend Tests (Python + pytest) (push) Has been cancelled
Continuous Integration / Docker Build Tests (agents) (push) Has been cancelled
Continuous Integration / Docker Build Tests (frontend) (push) Has been cancelled
Continuous Integration / Docker Build Tests (mcp) (push) Has been cancelled
Continuous Integration / Docker Build Tests (server) (push) Has been cancelled
Continuous Integration / Test Results Summary (push) Has been cancelled
refactor: reorganize features/shared directory for better maintainability (#730)
* refactor: reorganize features/shared directory structure

- Created organized subdirectories for better code organization:
  - api/ - API clients and HTTP utilities (renamed apiWithEtag.ts to apiClient.ts)
  - config/ - Configuration files (queryClient, queryPatterns)
  - types/ - Shared type definitions (errors)
  - utils/ - Pure utility functions (optimistic, clipboard)
  - hooks/ - Shared React hooks (already existed)

- Updated all import paths across the codebase (~40+ files)
- Updated all AI documentation in PRPs/ai_docs/ to reflect new structure
- All tests passing, build successful, no functional changes

This improves maintainability and follows vertical slice architecture patterns.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: address PR review comments and code improvements

- Update imports to use @/features alias path for optimistic utils
- Fix optimistic upload item replacement by matching on source_id instead of id
- Clean up test suite naming and remove meta-terms from comments
- Only set Content-Type header on requests with body
- Add explicit TypeScript typing to useProjectFeatures hook
- Complete Phase 4 improvements with proper query typing

* fix: address additional PR review feedback

- Clear feature queries when deleting project to prevent cache memory leaks
- Update KnowledgeCard comments to follow documentation guidelines
- Add explanatory comment for accessibility pattern in KnowledgeCard

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-22 14:59:33 +03:00

4.9 KiB

Optimistic Updates Pattern Guide

Core Architecture

Shared Utilities Module

Location: src/features/shared/utils/optimistic.ts

Provides type-safe utilities for managing optimistic state across all features:

  • createOptimisticId() - Generates stable UUIDs using nanoid
  • createOptimisticEntity<T>() - Creates entities with _optimistic and _localId metadata
  • isOptimistic() - Type guard for checking optimistic state
  • replaceOptimisticEntity() - Replaces optimistic items by _localId (race-condition safe)
  • removeDuplicateEntities() - Deduplicates after replacement
  • cleanOptimisticMetadata() - Strips optimistic fields when needed

TypeScript Interface

interface OptimisticEntity {
  _optimistic: boolean;
  _localId: string;
}

Implementation Patterns

Mutation Hooks Pattern

Reference: src/features/projects/tasks/hooks/useTaskQueries.ts:44-108

  1. onMutate: Create optimistic entity with stable ID

    • Use createOptimisticEntity<T>() for type-safe creation
    • Store optimisticId in context for later replacement
  2. onSuccess: Replace optimistic with server response

    • Use replaceOptimisticEntity() matching by _localId
    • Apply removeDuplicateEntities() to prevent duplicates
  3. onError: Rollback to previous state

    • Restore snapshot from context

UI Component Pattern

References:

  • src/features/projects/tasks/components/TaskCard.tsx:39-40,160,186
  • src/features/projects/components/ProjectCard.tsx:32-33,67,93
  • src/features/knowledge/components/KnowledgeCard.tsx:49-50,176,244
  1. Check optimistic state: const optimistic = isOptimistic(entity)
  2. Apply conditional styling: Add opacity and ring effect when optimistic
  3. Display indicator: Use <OptimisticIndicator> component for visual feedback

Visual Indicator Component

Location: src/features/ui/primitives/OptimisticIndicator.tsx

Reusable component showing:

  • Spinning loader icon (Loader2 from lucide-react)
  • "Saving..." text with pulse animation
  • Configurable via props: showSpinner, pulseAnimation

Feature Integration

Tasks

  • Mutations: src/features/projects/tasks/hooks/useTaskQueries.ts
  • UI: src/features/projects/tasks/components/TaskCard.tsx
  • Creates tasks with priority: "medium" default

Projects

  • Mutations: src/features/projects/hooks/useProjectQueries.ts
  • UI: src/features/projects/components/ProjectCard.tsx
  • Handles prd: null, data_schema: null for new projects

Knowledge

  • Mutations: src/features/knowledge/hooks/useKnowledgeQueries.ts
  • UI: src/features/knowledge/components/KnowledgeCard.tsx
  • Uses createOptimisticId() directly for progress tracking

Toasts

  • Location: src/features/shared/hooks/useToast.ts:43
  • Uses createOptimisticId() for unique toast IDs

Testing

Unit Tests

Location: src/features/shared/utils/tests/optimistic.test.ts

Covers all utility functions with 8 test cases:

  • ID uniqueness and format validation
  • Entity creation with metadata
  • Type guard functionality
  • Replacement logic
  • Deduplication
  • Metadata cleanup

Manual Testing Checklist

  1. Rapid Creation: Create 5+ items quickly - verify no duplicates
  2. Visual Feedback: Check optimistic indicators appear immediately
  3. ID Stability: Confirm nanoid-based IDs after server response
  4. Error Handling: Stop backend, attempt creation - verify rollback
  5. Race Conditions: Use browser console script for concurrent creates

Performance Characteristics

  • Bundle Impact: ~130 bytes (nanoid v5, minified+gzipped) - build/environment dependent
  • Update Speed: Typically snappy on modern devices; actual latency varies by device and workload
  • ID Generation: Per nanoid benchmarks: secure sync ≈5M ops/s, non-secure ≈2.7M ops/s, async crypto ≈135k ops/s
  • Memory: Minimal - only _optimistic and _localId metadata added per optimistic entity

Migration Notes

From Timestamp-based IDs

Before: const tempId = \temp-${Date.now()}`**After**:const optimisticId = createOptimisticId()`

Key Differences

  • No timestamp collisions during rapid creation
  • Stable IDs survive re-renders
  • Type-safe with full TypeScript inference
  • ~60% code reduction through shared utilities

Best Practices

  1. Always use shared utilities - Don't implement custom optimistic logic
  2. Match by _localId - Never match by the entity's id field
  3. Include deduplication - Always call removeDuplicateEntities() after replacement
  4. Show visual feedback - Users should see pending state clearly
  5. Handle errors gracefully - Always implement rollback in onError

Dependencies

  • nanoid: v5.0.9 - UUID generation
  • @tanstack/react-query: v5.x - Mutation state management
  • React: v18.x - UI components
  • TypeScript: v5.x - Type safety

Last updated: Phase 3 implementation (PR #695)