Skip to content

Commit b9c7af0

Browse files
authored
Fix: race condition on attachedGestures in updateHandlers (#3425)
## Description Our production apps are regularly crashing with the following stacktracke: ``` Non-fatal Exception: io.invertase.firebase.crashlytics.JavaScriptError: Cannot read property 'handlers' of undefined at .anonymous(address at node_modules/react-native-gesture-handler/src/handlers/gestures/GestureDetector/updateHandlers.ts:51:ghQueueMicrotask$argument_0) at .anonymous(address at node_modules/react-native/Libraries/Core/Timers/immediateShim.js:46:global.queueMicrotask$argument_0) ``` This seems to be caused by a race condition on the number of gestures in `preparedGesture.attachedGestures`. This PR fixes that race condition by storing the value of `preparedGesture.attachedGestures` from when the micro task was scheduled. ## Test plan Run example apps
1 parent b760f8e commit b9c7af0

File tree

1 file changed

+11
-3
lines changed

1 file changed

+11
-3
lines changed

src/handlers/gestures/GestureDetector/updateHandlers.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ export function updateHandlers(
3030
}
3131
}
3232

33+
// Store attached gestures to avoid crash when gestures changed after queueing micro task
34+
const attachedGestures = preparedGesture.attachedGestures;
35+
3336
// Use queueMicrotask to extract handlerTags, because when it's ran, all refs should be updated
3437
// and handlerTags in BaseGesture references should be updated in the loop above (we need to wait
3538
// in case of external relations)
@@ -38,12 +41,17 @@ export function updateHandlers(
3841
return;
3942
}
4043

44+
// Stop if attached gestures changed after queueing micro task
45+
if (attachedGestures !== preparedGesture.attachedGestures) {
46+
return;
47+
}
48+
4149
// If amount of gesture configs changes, we need to update the callbacks in shared value
4250
let shouldUpdateSharedValueIfUsed =
43-
preparedGesture.attachedGestures.length !== newGestures.length;
51+
attachedGestures.length !== newGestures.length;
4452

4553
for (let i = 0; i < newGestures.length; i++) {
46-
const handler = preparedGesture.attachedGestures[i];
54+
const handler = attachedGestures[i];
4755

4856
// If the gestureId is different (gesture isn't wrapped with useMemo or its dependencies changed),
4957
// we need to update the shared value, assuming the gesture runs on UI thread or the thread changed
@@ -70,7 +78,7 @@ export function updateHandlers(
7078
}
7179

7280
if (preparedGesture.animatedHandlers && shouldUpdateSharedValueIfUsed) {
73-
const newHandlersValue = preparedGesture.attachedGestures
81+
const newHandlersValue = attachedGestures
7482
.filter((g) => g.shouldUseReanimated) // Ignore gestures that shouldn't run on UI
7583
.map((g) => g.handlers) as unknown as HandlerCallbacks<
7684
Record<string, unknown>

0 commit comments

Comments
 (0)