diff --git a/src/hooks/useSyncSharedValue.ts b/src/hooks/useSyncSharedValue.ts new file mode 100644 index 00000000000..1b97e623c6b --- /dev/null +++ b/src/hooks/useSyncSharedValue.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { useAnimatedReaction, runOnJS } from 'react-native-reanimated'; +import { deepEqualWorklet, shallowEqualWorklet } from '@/worklets/comparisons'; + +type SetStateType = D extends 'sharedToState' ? (value: T) => void : never; + +interface SyncParams { + compareDepth?: 'shallow' | 'deep'; + setState: SetStateType; + sharedValue: { value: T }; + state: T; + syncDirection: D; +} + +export function useSyncSharedValue({ + compareDepth = 'shallow', + setState, + sharedValue, + state, + syncDirection, +}: SyncParams) { + useAnimatedReaction( + () => { + if (typeof sharedValue.value === 'object' && sharedValue.value !== null && typeof state === 'object' && state !== null) { + const isEqual = + compareDepth === 'deep' + ? deepEqualWorklet(sharedValue.value as Record, state as Record) + : shallowEqualWorklet(sharedValue.value as Record, state as Record); + return !isEqual; + } + return sharedValue.value !== state; + }, + shouldSync => { + if (shouldSync) { + if (syncDirection === 'sharedToState') { + runOnJS(setState)(sharedValue.value); + } else { + sharedValue.value = state; + } + } + } + ); +} diff --git a/src/worklets/comparisons.ts b/src/worklets/comparisons.ts new file mode 100644 index 00000000000..b0ed50453fb --- /dev/null +++ b/src/worklets/comparisons.ts @@ -0,0 +1,35 @@ +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +export function deepEqualWorklet(obj1: Record, obj2: Record): boolean { + 'worklet'; + if (Object.is(obj1, obj2)) { + return true; + } + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + if (keys1.length !== keys2.length) { + return false; + } + for (const key of keys1) { + if (!(key in obj2) || !deepEqualWorklet(obj1[key], obj2[key])) { + return false; + } + } + return true; +} + +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +export function shallowEqualWorklet(obj1: Record, obj2: Record): boolean { + 'worklet'; + if (Object.is(obj1, obj2)) { + return true; + } + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + if (keys1.length !== keys2.length) return false; + for (const key of keys1) { + if (obj1[key] !== obj2[key]) { + return false; + } + } + return true; +}