From cb307b9b020eef33130db976f10d50ee1d92fba2 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Mon, 19 Feb 2024 02:24:46 -0800 Subject: [PATCH] add synchronisation to Animated when used with setNativeProps (#43083) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/43083 changelog: [internal] See code comments for details. Reviewed By: yungsters Differential Revision: D53818488 fbshipit-source-id: 71a1636a635c4c6599313b0c44be7215e9bdbcb5 --- .../Libraries/Animated/useAnimatedProps.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/react-native/Libraries/Animated/useAnimatedProps.js b/packages/react-native/Libraries/Animated/useAnimatedProps.js index 10c2a340c0c..6be9fac9cd8 100644 --- a/packages/react-native/Libraries/Animated/useAnimatedProps.js +++ b/packages/react-native/Libraries/Animated/useAnimatedProps.js @@ -37,6 +37,7 @@ export default function useAnimatedProps( ): [ReducedProps, CallbackRef] { const [, scheduleUpdate] = useReducer(count => count + 1, 0); const onUpdateRef = useRef void>(null); + const timerRef = useRef(null); // TODO: Only invalidate `node` if animated props or `style` change. In the // previous implementation, we permitted `style` to override props with the @@ -87,6 +88,25 @@ export default function useAnimatedProps( // $FlowIgnore[not-a-function] - Assume it's still a function. // $FlowFixMe[incompatible-use] instance.setNativeProps(node.__getAnimatedValue()); + if (isFabricInstance(instance)) { + // Keeping state of Fiber tree and Shadow tree in sync. + // + // This is done by calling `scheduleUpdate` which will trigger a commit. + // However, React commit is not fast enough to drive animations. + // This is where setNativeProps comes in handy but the state between + // Fiber tree and Shadow tree needs to be kept in sync. + // The goal is to call `scheduleUpdate` as little as possible to maintain + // performance but frequently enough to keep state in sync. + // Debounce is set to 48ms, which is 3 * the duration of a frame. + // 3 frames was the highest value where flickering state was not observed. + if (timerRef.current != null) { + clearTimeout(timerRef.current); + } + timerRef.current = setTimeout(() => { + timerRef.current = null; + scheduleUpdate(); + }, 48); + } } };