From 4620dc71ed5984c48f3401edaeb53cc8e1ee5d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20=C5=BBelawski?= Date: Sun, 10 Nov 2024 22:21:49 +0100 Subject: [PATCH 1/2] fix(Android, Paper): Lost update on render --- .../createAnimatedComponent/getViewInfo.ts | 1 + .../src/hook/useAnimatedStyle.ts | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/getViewInfo.ts b/packages/react-native-reanimated/src/createAnimatedComponent/getViewInfo.ts index e97fb977094f..0c4b5ea2951c 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/getViewInfo.ts +++ b/packages/react-native-reanimated/src/createAnimatedComponent/getViewInfo.ts @@ -17,6 +17,7 @@ export let getViewInfo = (element: any) => { return getViewInfo73(element); }; +// This gets invoked on Paper on 0.76 function getViewInfo73(element: any) { return { // we can access view tag in the same way it's accessed here https://github.com/facebook/react/blob/e3f4eb7272d4ca0ee49f27577156b57eeb07cf73/packages/react-native-renderer/src/ReactFabric.js#L146 diff --git a/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts b/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts index 7f4f188f2dfb..1f8cdcdde853 100644 --- a/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts +++ b/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts @@ -35,6 +35,7 @@ import type { } from '../commonTypes'; import { isWorkletFunction } from '../commonTypes'; import { ReanimatedError } from '../errors'; +import { Platform } from 'react-native'; const SHOULD_BE_USE_WEB = shouldBeUseWeb(); @@ -43,6 +44,7 @@ interface AnimatedState { animations: AnimatedStyle; isAnimationRunning: boolean; isAnimationCancelled: boolean; + isFirstRun: boolean; } interface AnimatedUpdaterData { @@ -465,6 +467,7 @@ For more, see the docs: \`https://docs.swmansion.com/react-native-reanimated/doc animations: {}, isAnimationCancelled: false, isAnimationRunning: false, + isFirstRun: true, }), viewDescriptors: makeViewDescriptorsSet(), }; @@ -511,6 +514,36 @@ For more, see the docs: \`https://docs.swmansion.com/react-native-reanimated/doc areAnimationsActive, isAnimatedProps ); + if ( + Platform.OS === 'android' && + !globalThis._IS_FABRIC && + remoteState.isFirstRun + ) { + /* + This is a makeshift patch for a bug on Paper Android. + + When an Animated Component gets mounted and it already has an + update (i.e. animation starts on mount), the first update can be + lost. This is due to fact that Android UIManager is batching the + Native Views creation - an update might come before such batch + is executed, in result trying to modify a view which doesn't exist. + + After failing to force the UIManager + to create the View pre-update, I decided to instead schedule the + first update twice for Animated Style, because on another + frame the view should exist and the update should be successful. + */ + requestAnimationFrame(() => + styleUpdater( + shareableViewDescriptors, + updater, + remoteState, + areAnimationsActive, + isAnimatedProps + ) + ); + remoteState.isFirstRun = false; + } }; } const mapperId = startMapper(fun, inputs); From 9f3ae54c4a0cd2fab67177c40cc11782c0ae1f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20=C5=BBelawski?= Date: Sun, 10 Nov 2024 22:34:31 +0100 Subject: [PATCH 2/2] chore: Restore first run on deps change --- .../react-native-reanimated/src/hook/useAnimatedStyle.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts b/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts index 1f8cdcdde853..59cab4a8b7d5 100644 --- a/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts +++ b/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts @@ -2,7 +2,7 @@ import type { MutableRefObject } from 'react'; import { useEffect, useRef } from 'react'; -import { makeShareable, startMapper, stopMapper } from '../core'; +import { makeShareable, runOnUI, startMapper, stopMapper } from '../core'; import updateProps, { updatePropsJestWrapper } from '../UpdateProps'; import { initialUpdaterRun } from '../animation'; import { useSharedValue } from './useSharedValue'; @@ -549,6 +549,9 @@ For more, see the docs: \`https://docs.swmansion.com/react-native-reanimated/doc const mapperId = startMapper(fun, inputs); return () => { stopMapper(mapperId); + if (Platform.OS === 'android' && !globalThis._IS_FABRIC) { + runOnUI(() => (remoteState.isFirstRun = true))(); + } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, dependencies);