Skip to content

Commit f9a63c9

Browse files
mfazekasclaude
andcommitted
fix: stabilize property setter functions by using global options
Property setter functions (setValue, setButtonText, etc.) from useRive* hooks were unstable because the options object was recreated on each render, even though useCallback was used for getProperty. This caused unnecessary re-renders and listener resets. Extract options to global constants to ensure stable object references across renders. For useRiveTrigger, use useMemo to recreate options only when onTrigger changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2a7691b commit f9a63c9

File tree

5 files changed

+34
-20
lines changed

5 files changed

+34
-20
lines changed

src/hooks/useRiveBoolean.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import { useCallback } from 'react';
21
import {
32
type ViewModelBooleanProperty,
43
type ViewModelInstance,
54
} from '../specs/ViewModel.nitro';
65
import type { UseRivePropertyResult } from '../types';
76
import { useRiveProperty } from './useRiveProperty';
87

8+
const BOOLEAN_PROPERTY_OPTIONS = {
9+
getProperty: (vmi: ViewModelInstance, p: string) => vmi.booleanProperty(p),
10+
};
11+
912
/**
1013
* Hook for interacting with boolean ViewModel instance properties.
1114
*
@@ -20,8 +23,6 @@ export function useRiveBoolean(
2023
const [value, setValue, error] = useRiveProperty<
2124
ViewModelBooleanProperty,
2225
boolean
23-
>(viewModelInstance, path, {
24-
getProperty: useCallback((vmi, p) => vmi.booleanProperty(p), []),
25-
});
26+
>(viewModelInstance, path, BOOLEAN_PROPERTY_OPTIONS);
2627
return { value, setValue, error };
2728
}

src/hooks/useRiveColor.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { type UseRivePropertyResult } from '../types';
77
import { useRiveProperty } from './useRiveProperty';
88
import { RiveColor } from '../core/RiveColor';
99

10+
const COLOR_PROPERTY_OPTIONS = {
11+
getProperty: (vmi: ViewModelInstance, p: string) => vmi.colorProperty(p),
12+
};
13+
1014
/**
1115
* Hook for interacting with color ViewModel instance properties.
1216
*
@@ -23,9 +27,7 @@ export function useRiveColor(
2327
const [rawValue, setRawValue, error] = useRiveProperty<
2428
ViewModelColorProperty,
2529
number
26-
>(viewModelInstance, path, {
27-
getProperty: useCallback((vmi, p) => vmi.colorProperty(p), []),
28-
});
30+
>(viewModelInstance, path, COLOR_PROPERTY_OPTIONS);
2931

3032
const value =
3133
rawValue !== undefined ? RiveColor.fromInt(rawValue) : undefined;

src/hooks/useRiveNumber.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import { useCallback } from 'react';
21
import {
32
type ViewModelInstance,
43
type ViewModelNumberProperty,
54
} from '../specs/ViewModel.nitro';
65
import type { UseRivePropertyResult } from '../types';
76
import { useRiveProperty } from './useRiveProperty';
87

8+
const NUMBER_PROPERTY_OPTIONS = {
9+
getProperty: (vmi: ViewModelInstance, p: string) => vmi.numberProperty(p),
10+
};
11+
912
/**
1013
* Hook for interacting with number ViewModel instance properties.
1114
*
@@ -20,8 +23,6 @@ export function useRiveNumber(
2023
const [value, setValue, error] = useRiveProperty<
2124
ViewModelNumberProperty,
2225
number
23-
>(viewModelInstance, path, {
24-
getProperty: useCallback((vmi, p) => vmi.numberProperty(p), []),
25-
});
26+
>(viewModelInstance, path, NUMBER_PROPERTY_OPTIONS);
2627
return { value, setValue, error };
2728
}

src/hooks/useRiveString.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import { useCallback } from 'react';
21
import {
32
type ViewModelInstance,
43
type ViewModelStringProperty,
54
} from '../specs/ViewModel.nitro';
65
import type { UseRivePropertyResult } from '../types';
76
import { useRiveProperty } from './useRiveProperty';
87

8+
const STRING_PROPERTY_OPTIONS = {
9+
getProperty: (vmi: ViewModelInstance, p: string) => vmi.stringProperty(p),
10+
};
11+
912
/**
1013
* Hook for interacting with string ViewModel instance properties.
1114
*
@@ -20,8 +23,6 @@ export function useRiveString(
2023
const [value, setValue, error] = useRiveProperty<
2124
ViewModelStringProperty,
2225
string
23-
>(viewModelInstance, path, {
24-
getProperty: useCallback((vmi, p) => vmi.stringProperty(p), []),
25-
});
26+
>(viewModelInstance, path, STRING_PROPERTY_OPTIONS);
2627
return { value, setValue, error };
2728
}

src/hooks/useRiveTrigger.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback } from 'react';
1+
import { useCallback, useMemo } from 'react';
22
import {
33
type ViewModelInstance,
44
type ViewModelTriggerProperty,
@@ -9,6 +9,9 @@ import type {
99
} from '../types';
1010
import { useRiveProperty } from './useRiveProperty';
1111

12+
const getTriggerProperty = (vmi: ViewModelInstance, p: string) =>
13+
vmi.triggerProperty(p);
14+
1215
/**
1316
* Hook for interacting with trigger ViewModel instance properties.
1417
*
@@ -22,13 +25,19 @@ export function useRiveTrigger(
2225
params?: UseViewModelInstanceTriggerParameters
2326
): UseRiveTriggerResult {
2427
const { onTrigger } = params ?? {};
28+
29+
const triggerOptions = useMemo(
30+
() => ({
31+
getProperty: getTriggerProperty,
32+
onPropertyEventOverride: onTrigger,
33+
}),
34+
[onTrigger]
35+
);
36+
2537
const [_, __, error, property] = useRiveProperty<
2638
ViewModelTriggerProperty,
2739
undefined
28-
>(viewModelInstance, path, {
29-
getProperty: useCallback((vmi, p) => vmi.triggerProperty(p), []),
30-
onPropertyEventOverride: onTrigger,
31-
});
40+
>(viewModelInstance, path, triggerOptions);
3241

3342
const trigger = useCallback(() => {
3443
if (property) {

0 commit comments

Comments
 (0)