1- import { useCallback , useEffect , useState } from 'react' ;
1+ import { useCallback , useEffect , useState , useSyncExternalStore } from 'react' ;
22import type { LayoutChangeEvent } from 'react-native' ;
33import { AccessibilityInfo } from 'react-native' ;
44import moveAccessibilityFocus from './moveAccessibilityFocus' ;
@@ -18,50 +18,29 @@ const useScreenReaderStatus = (): boolean => {
1818 return isScreenReaderEnabled ;
1919} ;
2020
21- /**
22- * Hook that returns whether the user has enabled the "reduce motion" accessibility setting.
23- * This is used to disable animations for users who are sensitive to motion.
24- * Works on iOS, Android, and Web (via react-native-web which uses prefers-reduced-motion media query).
25- */
26- // Module-level cache so new hook instances start with the last known value
27- // instead of defaulting to false (which causes a race condition on remount)
2821let cachedReduceMotionValue = false ;
2922
30- const useReducedMotion = ( ) : boolean => {
31- const [ isReduceMotionEnabled , setIsReduceMotionEnabled ] = useState ( cachedReduceMotionValue ) ;
23+ function subscribeReduceMotion ( callback : ( ) => void ) {
24+ const subscription = AccessibilityInfo . addEventListener ( 'reduceMotionChanged' , ( enabled ) => {
25+ cachedReduceMotionValue = enabled ;
26+ callback ( ) ;
27+ } ) ;
3228
33- useEffect ( ( ) => {
34- let isMounted = true ;
35-
36- const subscription = AccessibilityInfo . addEventListener ( 'reduceMotionChanged' , ( enabled ) => {
29+ AccessibilityInfo . isReduceMotionEnabled ( )
30+ . then ( ( enabled ) => {
3731 cachedReduceMotionValue = enabled ;
38- setIsReduceMotionEnabled ( enabled ) ;
39- } ) ;
32+ callback ( ) ;
33+ } )
34+ . catch ( ( ) => { } ) ;
4035
41- AccessibilityInfo . isReduceMotionEnabled ( )
42- . then ( ( enabled ) => {
43- cachedReduceMotionValue = enabled ;
44- if ( ! isMounted ) {
45- return ;
46- }
47- setIsReduceMotionEnabled ( enabled ) ;
48- } )
49- . catch ( ( ) => {
50- // If the check fails, default to false (animations enabled)
51- if ( ! isMounted ) {
52- return ;
53- }
54- setIsReduceMotionEnabled ( false ) ;
55- } ) ;
36+ return ( ) => subscription ?. remove ( ) ;
37+ }
5638
57- return ( ) => {
58- isMounted = false ;
59- subscription ?. remove ( ) ;
60- } ;
61- } , [ ] ) ;
39+ function getReduceMotionSnapshot ( ) {
40+ return cachedReduceMotionValue ;
41+ }
6242
63- return isReduceMotionEnabled ;
64- } ;
43+ const useReducedMotion = ( ) : boolean => useSyncExternalStore ( subscribeReduceMotion , getReduceMotionSnapshot , ( ) => false ) ;
6544
6645const getHitSlopForSize = ( { x, y} : HitSlop ) => {
6746 /* according to https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/
0 commit comments