Skip to content

Conversation

@j-piasecki
Copy link
Member

@j-piasecki j-piasecki commented Nov 25, 2025

Description

Shouldn't need software-mansion/react-native-reanimated#8655 to work properly

Instead of using createAnimatedComponent, which does a lot more stuff other than setting up events, we can set up events ourselves using Reanimated's NativeEventsManager directly.

The implementation in the PR should be independent of the Reanimated version being used, as long as it's a relatively modern one.

Test plan

Tested on this stress-test
import React, { Profiler, useEffect, useRef } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { GestureDetector, usePanGesture } from 'react-native-gesture-handler';
// import { PerfMonitor } from 'react-native-gesture-handler/src/v3/PerfMonitor';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated';

const DATA = new Array(500).fill(null).map((_, i) => `Item ${i + 1}`);

function Item() {
  const translateX = useSharedValue(0);

  const style = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: translateX.value }],
    };
  });

  const pan = usePanGesture({
    // disableReanimated: true,
    onUpdate: (event) => {
      'worklet';
      console.log('pan onUpdate', event.handlerData.changeX);
    },
    onStart: () => {
      'worklet';
      console.log('pan onStart', _WORKLET);
    },
    onEnd: () => {
      'worklet';
      console.log('pan onEnd');
    },
    onBegin: () => {
      'worklet';
      console.log('pan onBegin');
    },
    onFinalize: () => {
      'worklet';
      console.log('pan onFinalize');
    },
    onTouchesDown: () => {
      'worklet';
      console.log('pan onTouchesDown');
    },
  });

  return (
    <View style={{ height: 80, padding: 16, backgroundColor: 'gray' }}>
      <GestureDetector gesture={pan}>
        {/* <View collapsable={false} style={{opacity: 0.5}}> */}
        <Animated.View
          style={[
            { backgroundColor: 'red', height: '100%', aspectRatio: 1 },
            style,
          ]}
        />
        {/* </View> */}
      </GestureDetector>
    </View>
  );
}

function Benchmark() {
  return (
    <ScrollView
      style={{ flex: 1 }}
      contentContainerStyle={{ flexGrow: 1, gap: 8 }}>
      {DATA.map((_, index) => (
        <Item key={index} />
      ))}
    </ScrollView>
  );
}

const TIMES = 35;

export default function EmptyExample() {
  const times = useRef<number[]>([]).current;
  const [visible, setVisible] = React.useState(false);

  useEffect(() => {
    if (!visible && times.length < TIMES) {
      setTimeout(() => {
        setVisible(true);
      }, 24);
    }

    if (times.length === TIMES) {
      // calculate average, but remove highest and lowest
      const sortedTimes = [...times].sort((a, b) => a - b);
      sortedTimes.shift();
      sortedTimes.shift();
      sortedTimes.pop();
      sortedTimes.pop();
      const avgTime =
        sortedTimes.reduce((sum, time) => sum + time, 0) / sortedTimes.length;

      console.log(`Average render time: ${avgTime} ms`);

      // console.log(JSON.stringify(PerfMonitor.getMeasures(), null, 2));
      // PerfMonitor.clear();
    }
  }, [visible]);

  return (
    <View style={styles.container}>
      {visible && (
        <Profiler
          id="v3"
          onRender={(_id, _phase, actualDuration) => {
            times.push(actualDuration);
            setTimeout(() => {
              setVisible(false);
            }, 24);
          }}>
          <Benchmark />
        </Profiler>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

Before: 614ms, after: 576ms

@j-piasecki j-piasecki force-pushed the @jpiasecki/useReanimatedEventsManager branch from fb55d18 to 29b96ce Compare November 28, 2025 14:42
@j-piasecki j-piasecki changed the title Listen for Reanimated events directly, without createAnimatedComponent [General] Listen for Reanimated events directly, without createAnimatedComponent when possible Nov 28, 2025
@j-piasecki j-piasecki marked this pull request as ready for review November 28, 2025 14:54
j-piasecki and others added 3 commits December 1, 2025 09:21
…tureDetector.tsx

Co-authored-by: Michał Bert <63123542+m-bert@users.noreply.github.com>
…tedNativeDetector.tsx

Co-authored-by: Michał Bert <63123542+m-bert@users.noreply.github.com>
@j-piasecki j-piasecki merged commit ae41fec into next Dec 1, 2025
2 checks passed
@j-piasecki j-piasecki deleted the @jpiasecki/useReanimatedEventsManager branch December 1, 2025 10:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants