Skip to content

Fix ConcurrentModificationException - #3691

Merged
m-bert merged 1 commit into
nextfrom
@mbert/fix-concurrent-modification-exception
Aug 26, 2025
Merged

Fix ConcurrentModificationException#3691
m-bert merged 1 commit into
nextfrom
@mbert/fix-concurrent-modification-exception

Conversation

@m-bert

@m-bert m-bert commented Aug 26, 2025

Copy link
Copy Markdown
Collaborator

Description

Currently in NativeDetector on android we have:

  fun onViewDrop() {
    val registry = RNGestureHandlerModule.registries[moduleId]
      ?: throw Exception("Tried to access a non-existent registry")

    for (tag in attachedHandlers) {
      registry.detachHandler(tag)
      attachedHandlers.remove(tag)
    }
  }

Under some circumstances (I've managed to reproduce it on my branch with gesture relations) it fails with ConcurrentModificationException

Test plan

I'll update when I push my branch to remote.

Tested on the following code:
import * as React from 'react';
import { Animated, Button, useAnimatedValue } from 'react-native';
import {
  GestureHandlerRootView,
  NativeDetector,
  useSimultaneous,
  useGesture,
  useExclusive,
  useRace,
} from 'react-native-gesture-handler';

export default function App() {
  const [visible, setVisible] = React.useState(true);

  const value = useAnimatedValue(0);
  const event = Animated.event(
    [{ nativeEvent: { handlerData: { translationX: value } } }],
    {
      useNativeDriver: true,
    }
  );

  const tap1 = useGesture('TapGestureHandler', {
    onEnd: () => {
      'worklet';
      console.log('Tap 1');
    },
    numberOfTaps: 1,
  });
  const tap2 = useGesture('TapGestureHandler', {
    onEnd: () => {
      'worklet';
      console.log('Tap 2');
    },
    numberOfTaps: 2,
  });

  const pan1 = useGesture('PanGestureHandler', {
    onUpdate: (e) => {
      'worklet';
      console.log('Pan 1');
    },
  });

  const pan2 = useGesture('PanGestureHandler', {
    onUpdate: (e) => {
      'worklet';
      console.log('Pan 2');
    },
  });

  // const composedGesture = useExclusive(tap2, tap1);
  const composedGesture = useRace(pan1, pan2);
  // const composedGesture = useExclusive(tap1, useSimultaneous(pan1, pan2));

  return (
    <GestureHandlerRootView
      style={{ flex: 1, backgroundColor: 'white', paddingTop: 8 }}>
      <Button
        title="Toggle visibility"
        onPress={() => {
          setVisible(!visible);
        }}
      />

      {visible && (
        <NativeDetector gesture={composedGesture}>
          <Animated.View
            style={[
              {
                width: 150,
                height: 150,
                backgroundColor: 'blue',
                opacity: 0.5,
                borderWidth: 10,
                borderColor: 'green',
                marginTop: 20,
                marginLeft: 40,
              },
              { transform: [{ translateX: value }] },
            ]}
          />
        </NativeDetector>
      )}
    </GestureHandlerRootView>
  );
}

@m-bert
m-bert merged commit d4b7ebe into next Aug 26, 2025
3 checks passed
@m-bert
m-bert deleted the @mbert/fix-concurrent-modification-exception branch August 26, 2025 11:41
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.

2 participants