diff --git a/packages/react-native-sortables/src/integrations/reanimated/hooks/useAnimatableValue.dependencies.test.ts b/packages/react-native-sortables/src/integrations/reanimated/hooks/useAnimatableValue.dependencies.test.ts new file mode 100644 index 00000000..21541391 --- /dev/null +++ b/packages/react-native-sortables/src/integrations/reanimated/hooks/useAnimatableValue.dependencies.test.ts @@ -0,0 +1,44 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useDerivedValue } from 'react-native-reanimated'; + +import useAnimatableValue from './useAnimatableValue'; + +let mockIsWeb = false; + +jest.mock('react-native-reanimated', () => ({ + isSharedValue: jest.fn(() => false), + useDerivedValue: jest.fn(() => ({ value: 0 })) +})); + +jest.mock('../../../constants/platform', () => ({ + get IS_WEB() { + return mockIsWeb; + } +})); + +describe(useAnimatableValue, () => { + const mockUseDerivedValue = jest.mocked(useDerivedValue); + + it('does not pass dependencies to useDerivedValue on native, where Reanimated warns about them', () => { + mockIsWeb = false; + + renderHook(() => useAnimatableValue(1)); + + expect(mockUseDerivedValue).toHaveBeenCalledWith( + expect.any(Function), + undefined + ); + }); + + it('passes the value and modifier as dependencies on web, where Reanimated needs them', () => { + mockIsWeb = true; + const modify = (value: number) => value * 2; + + renderHook(() => useAnimatableValue(1, modify)); + + expect(mockUseDerivedValue).toHaveBeenCalledWith(expect.any(Function), [ + 1, + modify + ]); + }); +}); diff --git a/packages/react-native-sortables/src/integrations/reanimated/hooks/useAnimatableValue.ts b/packages/react-native-sortables/src/integrations/reanimated/hooks/useAnimatableValue.ts index 65dc9f4a..031a36a9 100644 --- a/packages/react-native-sortables/src/integrations/reanimated/hooks/useAnimatableValue.ts +++ b/packages/react-native-sortables/src/integrations/reanimated/hooks/useAnimatableValue.ts @@ -4,6 +4,7 @@ import type { SharedValue } from 'react-native-reanimated'; import { isSharedValue, useDerivedValue } from 'react-native-reanimated'; +import { IS_WEB } from '../../../constants/platform'; import type { Animatable } from '../types'; export default function useAnimatableValue( @@ -19,8 +20,13 @@ export default function useAnimatableValue any>( value: Animatable, modify?: F ): SharedValue> | SharedValue { - return useDerivedValue(() => { - const inputValue = isSharedValue(value) ? value.value : value; - return modify ? modify(inputValue) : inputValue; - }, [value, modify]); + // Reanimated only uses dependencies on web and warns about them on native, where they are + // derived from the worklet closure + return useDerivedValue( + () => { + const inputValue = isSharedValue(value) ? value.value : value; + return modify ? modify(inputValue) : inputValue; + }, + IS_WEB ? [value, modify] : undefined + ); }