From 4de845b03f6d3ace07fac5b047dde3a1169b3bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 17 Dec 2025 12:45:35 +0100 Subject: [PATCH] feat: add functional updater support to setValue in property hooks --- src/hooks/useRiveColor.ts | 11 +++++++---- src/hooks/useRiveProperty.ts | 15 +++++++++++++-- src/index.tsx | 1 + src/types.tsx | 15 ++++++++++++--- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/hooks/useRiveColor.ts b/src/hooks/useRiveColor.ts index 3e9a9c30..e454bb69 100644 --- a/src/hooks/useRiveColor.ts +++ b/src/hooks/useRiveColor.ts @@ -3,7 +3,6 @@ import type { ViewModelColorProperty, ViewModelInstance, } from '../specs/ViewModel.nitro'; -import { type UseRivePropertyResult } from '../types'; import { useRiveProperty } from './useRiveProperty'; import { RiveColor } from '../core/RiveColor'; @@ -11,6 +10,12 @@ const COLOR_PROPERTY_OPTIONS = { getProperty: (vmi: ViewModelInstance, p: string) => vmi.colorProperty(p), }; +export interface UseRiveColorResult { + value: RiveColor | undefined; + setValue: (value: RiveColor | string) => void; + error: Error | null; +} + /** * Hook for interacting with color ViewModel instance properties. * @@ -21,9 +26,7 @@ const COLOR_PROPERTY_OPTIONS = { export function useRiveColor( path: string, viewModelInstance?: ViewModelInstance | null -): UseRivePropertyResult & { - setValue: (value: RiveColor | string) => void; -} { +): UseRiveColorResult { const [rawValue, setRawValue, error] = useRiveProperty< ViewModelColorProperty, number diff --git a/src/hooks/useRiveProperty.ts b/src/hooks/useRiveProperty.ts index fed29ab8..96141314 100644 --- a/src/hooks/useRiveProperty.ts +++ b/src/hooks/useRiveProperty.ts @@ -28,7 +28,12 @@ export function useRiveProperty

( /** Optional override callback for property events (mainly used by triggers) */ onPropertyEventOverride?: (...args: any[]) => void; } -): [T | undefined, (value: T) => void, Error | null, P | undefined] { +): [ + T | undefined, + (value: T | ((prevValue: T | undefined) => T)) => void, + Error | null, + P | undefined, +] { const [value, setValue] = useState(undefined); const [error, setError] = useState(null); @@ -79,7 +84,7 @@ export function useRiveProperty

( // Set the value of the property const setPropertyValue = useCallback( - (newValue: T) => { + (valueOrUpdater: T | ((prevValue: T | undefined) => T)) => { if (!property) { setError( new Error( @@ -87,6 +92,12 @@ export function useRiveProperty

( ) ); } else { + const newValue = + typeof valueOrUpdater === 'function' + ? (valueOrUpdater as (prevValue: T | undefined) => T)( + property.value + ) + : valueOrUpdater; property.value = newValue; } }, diff --git a/src/index.tsx b/src/index.tsx index 5896add0..e58c65d5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -50,4 +50,5 @@ export { useRiveColor } from './hooks/useRiveColor'; export { useRiveTrigger } from './hooks/useRiveTrigger'; export { useRiveFile } from './hooks/useRiveFile'; export { type RiveFileInput } from './hooks/useRiveFile'; +export { type SetValueAction } from './types'; export { DataBindMode }; diff --git a/src/types.tsx b/src/types.tsx index 2fd165bf..e7baf2b5 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -1,13 +1,22 @@ +/** + * A value or a function that computes a new value from the previous value. + * Similar to React's SetStateAction pattern. + */ +export type SetValueAction = T | ((prevValue: T | undefined) => T); + export interface UseRivePropertyResult { /** * The current value of the property. */ value: T | undefined; /** - * Set the value of the property. - * @param value - The value to set the property to. + * Set the value of the property. Accepts either a direct value or + * a function that receives the previous value and returns the new value. + * @example + * setValue(10) // Set to 10 + * setValue((prev) => (prev ?? 0) + 5) // Increment by 5 */ - setValue: (value: T) => void; + setValue: (value: SetValueAction) => void; /** * The error if the property is not found. */