From c7e1976bfe1ed8df6b42f11644faf4b1476e7254 Mon Sep 17 00:00:00 2001 From: Matthew Horan Date: Wed, 20 Nov 2024 18:43:13 -0500 Subject: [PATCH] fix: only intercept events with waiting handlers Previously, NativeReanimatedModule::handleRawEvent would intercept all events received by the event listener. This resulted in an issue where onLayout would not fire in JS on the New Architecture. Instead, only intercept events with waiting handlers. This prevents asJSIValue from being called on the Reanimated event loop and allows onLayout to bubble up in JS. See https://github.com/facebook/react-native/blob/v0.76.2/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewEventEmitter.cpp#L82-L112, which prevents onLayout from being dispatched more than once. asJSIValue evaluates the lambda above in https://github.com/facebook/react-native/blob/v0.76.2/packages/react-native/ReactCommon/react/renderer/core/ValueFactoryEventPayload.cpp#L16. Fixes #6684 --- .../RuntimeTests/RuntimeTestsExample.tsx | 2 + .../RuntimeTests/tests/core/onLayout.test.tsx | 46 +++++++++++++++++++ .../NativeModules/NativeReanimatedModule.cpp | 5 ++ 3 files changed, 53 insertions(+) create mode 100644 apps/common-app/src/examples/RuntimeTests/tests/core/onLayout.test.tsx diff --git a/apps/common-app/src/examples/RuntimeTests/RuntimeTestsExample.tsx b/apps/common-app/src/examples/RuntimeTests/RuntimeTestsExample.tsx index 6a460dbf4ef..4ad23f2af2d 100644 --- a/apps/common-app/src/examples/RuntimeTests/RuntimeTestsExample.tsx +++ b/apps/common-app/src/examples/RuntimeTests/RuntimeTestsExample.tsx @@ -51,6 +51,8 @@ export default function RuntimeTestsExample() { require('./tests/core/useDerivedValue/chain.test'); require('./tests/core/useSharedValue/animationsCompilerApi.test'); + + require('./tests/core/onLayout.test'); }, }, { diff --git a/apps/common-app/src/examples/RuntimeTests/tests/core/onLayout.test.tsx b/apps/common-app/src/examples/RuntimeTests/tests/core/onLayout.test.tsx new file mode 100644 index 00000000000..e132c1cfb68 --- /dev/null +++ b/apps/common-app/src/examples/RuntimeTests/tests/core/onLayout.test.tsx @@ -0,0 +1,46 @@ +import { useEffect } from 'react'; +import { StyleSheet, View } from 'react-native'; +import Animated, { runOnUI, useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; +import { describe, expect, render, test, wait } from '../../ReJest/RuntimeTestsApi'; + +let height = 0; + +const TestComponent = ({}) => { + const sv = useSharedValue(styles.smallBox.height); + + const onLayout = event => { + height = event.nativeEvent.layout.height; + }; + + const animatedStyle = useAnimatedStyle(() => { + return { height: sv.value }; + }); + + useEffect(() => { + runOnUI(() => { + sv.value += 100; + })(); + }); + + return ( + + + + ); +}; + +describe('onLayout', () => { + test('is not intercepted', async () => { + await render(); + await wait(100); + expect(height).toBe(200); + }); +}); + +const styles = StyleSheet.create({ + smallBox: { + width: 100, + height: 100, + backgroundColor: 'pink', + }, +}); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp index 404ca6b6493..a6782f6f793 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/NativeReanimatedModule.cpp @@ -613,6 +613,11 @@ bool NativeReanimatedModule::handleRawEvent( if (eventType.rfind("top", 0) == 0) { eventType = "on" + eventType.substr(3); } + + if (!isAnyHandlerWaitingForEvent(eventType, tag)) { + return false; + } + jsi::Runtime &rt = uiWorkletRuntime_->getJSIRuntime(); const auto &eventPayload = rawEvent.eventPayload; jsi::Value payload = eventPayload->asJSIValue(rt);