Skip to content

Epic: Eliminate $effect Anti-patterns - Embrace Svelte 5 Reactivity #623

Description

@malibio

Overview

The frontend codebase has accumulated 64+ $effect blocks, many of which represent anti-patterns that fight against Svelte 5's reactive model rather than embracing it. This epic tracks the systematic refactoring effort to eliminate unnecessary effects and adopt idiomatic Svelte 5 patterns.

Problem Statement

Current State:

  • 64 $effect usages across the codebase
  • Hotspots: base-node.svelte (13), base-node-viewer.svelte (9), header-node.svelte (6)
  • TextareaController class requires manual prop synchronization via effects
  • Effects used as event handlers, for derived state, and for lifecycle management

Root Cause:
The TextareaController class is an imperative escape hatch that doesn't participate in Svelte's reactivity system. Every prop that needs to reach the controller requires an $effect bridge for synchronization.

Anti-patterns Identified:

  1. Effects as event handlers - Watching state to trigger actions instead of calling functions from events
  2. Effects for derived state - Setting innerHTML/values imperatively instead of using $derived
  3. Effects as lifecycle hooks - Creating/destroying objects based on element existence
  4. Effects for prop synchronization - Manually syncing props to external class instances

Proposed Solution

Refactor in independent, testable pieces:

  1. Convert imperative controller patterns to reactive .svelte.ts modules
  2. Replace effect-based derived state with $derived
  3. Move event-triggered logic to actual event handlers
  4. Eliminate prop sync effects by making controllers reactive

Success Criteria

  • Reduce $effect count by 50%+ (from 64 to <32)
  • All existing tests continue to pass
  • No regression in edit mode, focus management, or autocomplete behavior
  • Code is more readable and follows Svelte 5 idioms

Child Issues

Phase 1 (Completed ✅)

Issue Description Status
#624 Refactor TextareaController to reactive .svelte.ts module ✅ Done
#625 Convert view mode HTML rendering from $effect to $derived ✅ Done
#626 Replace autocomplete and slash command effects with event-driven updates ✅ Done
#627 Eliminate calendar date selection effect - use onchange handler ✅ Done
#628 Audit and simplify base-node-viewer effects ✅ Done
#629 Audit and simplify header-node effects ✅ Done

Phase 2 (New - Final Cleanup)

Issue Description Effects to Eliminate
#693 Replace internalContent prop sync effects with $derived 4
#694 Eliminate date-node-viewer onTitleChange callback effect 1
#695 Optimize textarea-controller content/config sync effects 2

Phase 2 target: Reduce from 16 effects to 6 legitimate effects

Current Progress (Post-Phase 1 Audit)

Effects reduced: 64 → 16 (75% reduction)

Remaining 16 effects:

  • 6 LEGITIMATE (DOM lifecycle, async loading, subscriptions with cleanup)
  • 10 ELIMINATE-able (prop sync, callbacks) - tracked in Phase 2 issues

Technical Specifications

Legitimate Effects (Keep These)

  • base-node.svelte:742 - Controller initialization (DOM lifecycle)
  • code-block-node.svelte:235 - Click-outside handler (document event subscription)
  • custom-entity-node.svelte:61 - Async schema loading
  • pane-content.svelte:65 - Dynamic component loading
  • schema-property-form.svelte:50 - Store subscription with cleanup
  • schema-property-form.svelte:86 - Async schema loading

Reference Documentation

  • Svelte 5 runes documentation
  • Existing .svelte.ts patterns in codebase

Non-Goals

  • Rewriting component logic or features
  • Changing user-facing behavior
  • Refactoring non-effect-related code

Definition of Done

  • All Phase 1 child issues completed ✅
  • All Phase 2 child issues completed
  • Effect count reduced to ~6 legitimate effects
  • All tests pass
  • Code review completed for each piece

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions