feat(28-01): create useVuStore React hooks
- Add useAllVuLevels hook for subscribing to all VU levels - Add useVuLevel hook for efficient single-mixer subscription - Use useSyncExternalStore shim for React 16 compatibility - Memoize getSnapshot callback to avoid unnecessary resubscription Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5da85c1e79
commit
834472127d
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* React hooks for subscribing to the external VU store
|
||||
*
|
||||
* Uses useSyncExternalStore shim for React 16 compatibility.
|
||||
* Provides efficient subscription patterns for VU meter components.
|
||||
*/
|
||||
|
||||
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
||||
import { useCallback } from 'react';
|
||||
import { vuStore } from '../stores/vuStore';
|
||||
|
||||
/**
|
||||
* Subscribe to all VU levels
|
||||
*
|
||||
* WARNING: Use sparingly - causes re-render on ANY VU change.
|
||||
* Prefer useVuLevel for single mixer subscription.
|
||||
*
|
||||
* @returns {Object} - Map of mixerId to { level, clipping, timestamp }
|
||||
*
|
||||
* @example
|
||||
* function AllVuMeters() {
|
||||
* const allLevels = useAllVuLevels();
|
||||
* return Object.keys(allLevels).map(mixerId => (
|
||||
* <VuMeter key={mixerId} mixerId={mixerId} level={allLevels[mixerId]} />
|
||||
* ));
|
||||
* }
|
||||
*/
|
||||
export function useAllVuLevels() {
|
||||
return useSyncExternalStore(
|
||||
vuStore.subscribe,
|
||||
vuStore.getSnapshot
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to single mixer's VU level
|
||||
*
|
||||
* More efficient than useAllVuLevels - only re-renders when this specific
|
||||
* mixer's level changes.
|
||||
*
|
||||
* @param {string} mixerId - Qualified mixer ID (e.g., "M123" or "P456")
|
||||
* @returns {Object|null} - { level, clipping, timestamp } or null if mixer not found
|
||||
*
|
||||
* @example
|
||||
* function VuMeter({ mixerId }) {
|
||||
* const vuData = useVuLevel(mixerId);
|
||||
* if (!vuData) return null;
|
||||
* return <div>Level: {vuData.level}</div>;
|
||||
* }
|
||||
*/
|
||||
export function useVuLevel(mixerId) {
|
||||
// Create stable getSnapshot callback
|
||||
// CRITICAL: Must be memoized with useCallback to avoid unnecessary resubscription
|
||||
const getSnapshot = useCallback(() => vuStore.getLevelSnapshot(mixerId), [mixerId]);
|
||||
|
||||
return useSyncExternalStore(
|
||||
vuStore.subscribe,
|
||||
getSnapshot
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue