UNPKG

rough-native

Version:

Create graphics using HTML Canvas or SVG with a hand-drawn, sketchy, appearance. Features comprehensive React hooks, memory management, and React 18 concurrent rendering support.

255 lines (203 loc) โ€ข 7.36 kB
# React 18 Concurrent Rendering Support This document describes the React 18 concurrent rendering safety features implemented in rough-native's React hooks. ## ๐Ÿš€ Overview React 18 introduced concurrent rendering features that can cause **tearing** and **inconsistent state** in hooks that manage external state. Our implementation uses `useSyncExternalStore` to ensure visual consistency during concurrent updates. ## โšก Key Features ### Concurrent-Safe Instance Management ```typescript // โœ… Concurrent-safe rough instance creation const rough = useRough(config); // No tearing during config updates ``` ### Cached Shape Generation ```typescript // โœ… Concurrent-safe shape caching const shape = useRoughShape('polygon', points, options); // Consistent snapshots ``` ### Batch Processing Safety ```typescript // โœ… Concurrent-safe batch rendering const shapes = useRoughShapes(shapeDefinitions); // Atomic batch updates ``` ## ๐Ÿ—๏ธ Architecture ### RoughInstanceStore Manages RoughReactNativeSVG instances with concurrent safety: - **Immutable snapshots** prevent tearing - **Subscriber pattern** for consistent updates - **Automatic cleanup** prevents memory leaks ```typescript class RoughInstanceStore { private instances = new Map<string, RoughReactNativeSVG>(); private listeners = new Set<() => void>(); getSnapshot(): Map<string, RoughReactNativeSVG> { // Return immutable snapshot for concurrent safety return new Map(this.instances); } subscribe(callback: () => void): () => void { this.listeners.add(callback); return () => this.listeners.delete(callback); } } ``` ### ShapeGenerationStore Manages shape generation with caching and concurrent safety: - **Cached results** prevent duplicate work - **Pending generation tracking** avoids race conditions - **Error caching** prevents retry storms ```typescript class ShapeGenerationStore { private cache = new Map<string, any>(); private pendingGenerations = new Set<string>(); generateShape(key: string, generator: () => any): any { // Check cache first if (this.cache.has(key)) { return this.cache.get(key); } // Prevent duplicate generation if (this.pendingGenerations.has(key)) { return null; // Still generating } // Generate and cache result this.pendingGenerations.add(key); const result = generator(); this.cache.set(key, result); this.pendingGenerations.delete(key); return result; } } ``` ## ๐Ÿ”ง Concurrent Hook Implementation ### useRough with useSyncExternalStore ```typescript export function useRough(config?: Config): RoughReactNativeSVG { const stableConfig = useDeepMemo(config, [config]); // Concurrent-safe external store subscription const storeSnapshot = useSyncExternalStore( roughStore.subscribe.bind(roughStore), roughStore.getSnapshot.bind(roughStore), roughStore.getSnapshot.bind(roughStore) // SSR snapshot ); const rough = useMemo(() => { if (!instanceIdRef.current) { instanceIdRef.current = roughStore.createInstance(stableConfig); } else { roughStore.updateInstance(instanceIdRef.current, stableConfig); } return roughStore.getInstance(instanceIdRef.current); }, [stableConfig, storeSnapshot]); return rough; } ``` ### useRoughShape with Concurrent Caching ```typescript export function useRoughShape<T extends keyof ShapeParams>( shapeType: T, params: ShapeParams[T], options?: Options ) { const rough = useRough(); const shapeKey = useMemo(() => `${shapeType}-${JSON.stringify(params)}-${JSON.stringify(options)}` , [shapeType, params, options]); // Concurrent-safe shape generation const storeSnapshot = useSyncExternalStore( shapeStore.subscribe.bind(shapeStore), shapeStore.getSnapshot.bind(shapeStore), shapeStore.getSnapshot.bind(shapeStore) ); const shape = useMemo(() => { return shapeStore.generateShape(shapeKey, () => { // Shape generation logic return rough[shapeType](params, options); }); }, [shapeKey, rough, storeSnapshot]); return shape || { props: {}, children: [] }; } ``` ## ๐ŸŽฏ Benefits ### Visual Consistency - **No tearing** during concurrent updates - **Consistent snapshots** across component tree - **Atomic updates** for batch operations ### Performance - **Cached results** prevent duplicate work - **Efficient subscriptions** minimize re-renders - **Smart caching** with automatic cleanup ### Developer Experience - **Transparent usage** - works like regular hooks - **Debug utilities** for monitoring performance - **Error handling** with graceful degradation ## ๐Ÿ” Debugging Tools ### Performance Monitoring ```typescript import { debugUtils } from 'rough-native'; // Get cache statistics const deepEqualStats = debugUtils.getDeepEqualStats(); console.log(`Hit rate: ${deepEqualStats.hitRate * 100}%`); // Monitor instance count const instanceCount = debugUtils.getRoughInstanceCount(); console.log(`Active instances: ${instanceCount}`); // Check shape cache size const cacheSize = debugUtils.getShapeCacheSize(); console.log(`Cached shapes: ${cacheSize}`); ``` ### Cache Management ```typescript // Clear all caches for testing debugUtils.clearAllCaches(); // Clear specific caches debugUtils.clearRoughInstances(); debugUtils.clearShapeCache(); debugUtils.clearDeepEqualCache(); ``` ## ๐Ÿงช Usage Examples ### Concurrent Updates with useTransition ```tsx import React, { useTransition, startTransition } from 'react'; import { useRoughShape } from 'rough-native'; function ConcurrentShapeDemo() { const [isPending, startTransition] = useTransition(); const [complexity, setComplexity] = useState(5); // โœ… Concurrent-safe shape generation const shape = useRoughShape('polygon', generatePoints(complexity)); const handleComplexityChange = () => { startTransition(() => { setComplexity(c => c * 2); // Large update marked as transition }); }; return ( <View> <Button title="Increase Complexity" onPress={handleComplexityChange} /> {isPending ? <Text>Updating...</Text> : null} <Svg>{/* Render shape without tearing */}</Svg> </View> ); } ``` ### Batch Concurrent Processing ```tsx function ConcurrentBatchDemo() { const [shapes, setShapes] = useState(initialShapes); // โœ… Concurrent-safe batch processing const renderedShapes = useRoughShapes(shapes); const handleBatchUpdate = () => { startTransition(() => { setShapes(generateComplexShapes()); // Large batch update }); }; return <Svg>{/* All shapes render consistently */}</Svg>; } ``` ## ๐Ÿ”’ Concurrent Safety Guarantees ### Tearing Prevention - **Immutable snapshots** ensure visual consistency - **Synchronized updates** across component tree - **Atomic state changes** prevent partial updates ### Race Condition Protection - **Pending generation tracking** prevents duplicate work - **Cache consistency** during concurrent access - **Error isolation** prevents cascade failures ### Memory Safety - **WeakMap usage** allows garbage collection - **Automatic cleanup** on component unmount - **Bounded caches** prevent memory bloat The concurrent rendering implementation ensures that rough-native hooks work seamlessly with React 18's concurrent features while maintaining excellent performance and visual consistency.