Skip to content

Commit

Permalink
fix: only intercept events with waiting handlers
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mhoran committed Nov 21, 2024
1 parent 254af50 commit 6784b78
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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');
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<View onLayout={onLayout}>
<Animated.View style={[styles.smallBox, animatedStyle]} onLayout={animatedOnLayout} />
</View>
);
};

describe('onLayout', () => {
test('is not intercepted when there are no registered event handlers', async () => {
const result = {} as TestResult;
await render(<TestComponent result={result} />);
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(<TestComponent result={result} />);
await wait(500);
const { animatedHandlerCalled } = result;
expect(animatedHandlerCalled).toBe(true);
});
});

const styles = StyleSheet.create({
smallBox: {
width: 100,
height: 100,
backgroundColor: 'pink',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 6784b78

Please sign in to comment.