@@ -65,12 +65,32 @@ export default function useAnimatedHighlightStyle({
65
65
const { didScreenTransitionEnd} = useScreenWrapperTransitionStatus ( ) ;
66
66
const theme = useTheme ( ) ;
67
67
68
- const highlightBackgroundStyle = useAnimatedStyle ( ( ) => ( {
69
- backgroundColor : interpolateColor ( repeatableProgress . get ( ) , [ 0 , 1 ] , [ backgroundColor ?? theme . appBG , highlightColor ?? theme . border ] ) ,
70
- height : height ? interpolate ( nonRepeatableProgress . get ( ) , [ 0 , 1 ] , [ 0 , height ] ) : 'auto' ,
71
- opacity : interpolate ( nonRepeatableProgress . get ( ) , [ 0 , 1 ] , [ 0 , 1 ] ) ,
72
- borderRadius,
73
- } ) ) ;
68
+ const highlightBackgroundStyle = useAnimatedStyle ( ( ) => {
69
+ 'worklet' ;
70
+
71
+ // This code runs both on the JS thread (once for setup) and on the UI thread (for actual animation).
72
+ // To avoid accessing Reanimated shared values (like .value) on the JS thread,
73
+ // we guard the JS execution path with `_WORKLET`. This ensures we return a safe fallback style
74
+ // during the JS pass and avoid any crashes or unexpected behavior.
75
+ if ( ! _WORKLET ) {
76
+ return {
77
+ backgroundColor : backgroundColor ?? theme . appBG ,
78
+ height : height ?? 'auto' ,
79
+ opacity : 0 ,
80
+ borderRadius,
81
+ } ;
82
+ }
83
+
84
+ const repeatableValue = repeatableProgress . get ( ) ;
85
+ const nonRepeatableValue = nonRepeatableProgress . get ( ) ;
86
+
87
+ return {
88
+ backgroundColor : interpolateColor ( repeatableValue , [ 0 , 1 ] , [ backgroundColor ?? theme . appBG , highlightColor ?? theme . border ] ) ,
89
+ height : height ? interpolate ( nonRepeatableValue , [ 0 , 1 ] , [ 0 , height ] ) : 'auto' ,
90
+ opacity : interpolate ( nonRepeatableValue , [ 0 , 1 ] , [ 0 , 1 ] ) ,
91
+ borderRadius,
92
+ } ;
93
+ } , [ borderRadius , height , backgroundColor , highlightColor , theme . appBG , theme . border ] ) ;
74
94
75
95
React . useEffect ( ( ) => {
76
96
if ( ! shouldHighlight || startHighlight ) {
0 commit comments