- Updated version from v1.5 to v2.0 in both the LemonSpace Manifest and PRD documents. - Expanded Phase 1 scope to include video and asset nodes, and integrated non-destructive image editing capabilities. - Enhanced node taxonomy to reflect 6 categories with 27 node types. - Added details on offline sync features and optimistic updates in the documentation. - Improved clarity and structure of the product vision and problem statement sections.
4.4 KiB
hooks/ — Custom React Hooks
Geteilte React-Hooks. Nur client-side ("use client").
Hooks im Überblick
use-auth-query.ts
useAuthQuery(query, ...args)
Wrapper um Convex useQuery, der automatisch "skip" setzt wenn der Auth-Token noch nicht bereit ist. Verhindert Unauthenticated-Fehler bei Queries mit requireAuth im Backend.
Warum nicht direkt useQuery? Ohne initialToken würde Convex kurz eine unauthentifizierte Query feuern und eine Error-Toast anzeigen. Mit useAuthQuery wird gewartet bis isAuthenticated === true.
Wann nutzen: Immer wenn eine Convex-Query requireAuth aufruft. Für öffentliche Queries ist normales useQuery in Ordnung.
use-centered-flow-node-position.ts
Berechnet die Canvas-Position für einen neuen Node, sodass er im aktuellen Viewport-Zentrum erscheint. Wird beim Einfügen eines Nodes aus der Palette oder Command Palette genutzt.
Verwendung:
const centeredPosition = useCenteredFlowNodePosition(viewport)
const onNodeDragStop = (node) => {
// Node wird im Viewport-Zentrum erstellt
}
use-debounced-callback.ts
Standard-Debounce-Hook. Wird für teure Operationen wie Canvas-Snapshots und Convex-Mutations beim Resizen/Bewegen von Nodes verwendet.
Verwendung:
const debouncedUpdate = useDebouncedCallback(() => {
// Nur alle 300ms ausgeführt
updateCanvasSnapshot()
}, 300)
use-ai-generation-status.ts
Hook für AI-Generation-Status-Tracking. Überwacht Node-Status-Änderungen und zeigt Toasts bei Fehlern.
Features:
- Schwellenwert-basiertes Fehler-Tracking
- Auto-Hiding Toasts nach 5 Sekunden
- Fehler-Kategorisierung (Credits, Timeout, Provider, etc.)
Verwendung:
const { status, statusMessage } = useAiGenerationStatus()
use-adjustment-presets.ts
Hook für Adjustment-Preset-Management. Bündelt Preset-Queries und bietet helper-Funktionen für Preset-Validierung.
Verwendung:
const { presets, isLoading } = useAdjustmentPresets(nodeType)
const savePreset = (name, params) => { /* ... */ }
use-canvas-sync-status.ts
Hook für Canvas-Sync-Status (Online/Offline). Zeigt Banner oder Icons basierend auf der Verbindungsqualität.
Verwendung:
const { isOnline, pendingOps, syncStatus } = useCanvasSyncStatus()
Konventionen
- Hooks immer mit
use-Prefix im Dateinamen - Nur wiederverwendbare Hooks hier — canvas-spezifische Inline-Logik bleibt in
canvas.tsx - Kein direkter Convex-Zugriff in Hooks wenn möglich — Queries/Mutations von der aufrufenden Komponente übergeben lassen
- Hooks immer mit
"use client"am Anfang der Datei markieren - TypeScript-Typen immer definieren und exportieren
- Hooks dokumentieren mit JSDoc für bessere IDE-Unterstützung
Best Practices
- Keine Side Effects außerhalb von useEffect: Alle Nebeneffekte in useEffect oder custom hooks implementieren
- Type Safety: Hook-Props und Return-Werte immer typisieren
- Performance: useMemo und useCallback für teure Berechnungen nutzen
- Error Handling: Hooks sollten keine Exceptions werfen, sondern Fehler via Callbacks propagieren
- Dependencies: useMemo/useCallback Dependencies immer korrekt angeben
- Testing: Hooks sollen unit-testbar sein (keine React-Abhängigkeiten außer Callbacks)
Examples
Custom Hook für Node-Resizing
// hooks/use-node-resize.ts
export function useNodeResize(nodeId, updateNode) {
const debouncedUpdate = useDebouncedCallback(
(newData) => updateNode(nodeId, newData),
200
)
const handleResize = (newDimensions) => {
debouncedUpdate({
width: newDimensions.width,
height: newDimensions.height,
})
}
return { handleResize }
}
Custom Hook für LocalStorage-Backups
// hooks/use-local-storage.ts
export function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch (error) {
return initialValue
}
})
const setValue = (value: T) => {
try {
setStoredValue(value)
window.localStorage.setItem(key, JSON.stringify(value))
} catch (error) {
console.error(error)
}
}
return [storedValue, setValue] as const
}