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..7d74fecaf55 --- /dev/null +++ b/apps/common-app/src/examples/RuntimeTests/tests/core/onLayout.test.tsx @@ -0,0 +1,69 @@ +import { useEffect, useRef } from 'react'; +import type { LayoutChangeEvent } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import Animated, { runOnJS, runOnUI, useAnimatedStyle, useEvent, useSharedValue } from 'react-native-reanimated'; +import { describe, expect, render, test, wait } from '../../ReJest/RuntimeTestsApi'; + +interface TestResult { + height: number, + animatedHandlerCalled: boolean +} + +const TestComponent = ({result}: {result: TestResult}) => { + const sv = useSharedValue(styles.smallBox.height); + + const onLayout = (event: LayoutChangeEvent) => { + result.height = event.nativeEvent.layout.height; + }; + + const animatedStyle = useAnimatedStyle(() => { + return { height: sv.value }; + }); + + useEffect(() => { + runOnUI(() => { + sv.value += 100; + })(); + }); + + const setAnimatedHandlerCalled = () => { + result.animatedHandlerCalled = true; + }; + + const animatedOnLayout = useEvent(() => { + 'worklet'; + runOnJS(setAnimatedHandlerCalled)(); + }, ['onLayout']); + + return ( + + + + ); +}; + +describe('onLayout', () => { + test('is not intercepted when there are no registered event handlers', async () => { + const result = {} as TestResult; + await render(); + await wait(500); + const { height } = result; + expect(height).toBe(200); + }); + + test('is dispatched to the registered event handler', async () => { + const result = {} as TestResult; + await render(); + await wait(500); + const { animatedHandlerCalled } = result; + expect(animatedHandlerCalled).toBe(true); + }); +}); + +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);