-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Labels
Platform: AndroidThis issue is specific to AndroidThis issue is specific to AndroidPlatform: iOSThis issue is specific to iOSThis issue is specific to iOSRepro providedA reproduction with a snippet of code, snack or repo is providedA reproduction with a snippet of code, snack or repo is provided
Description
Description
extractInputs in mappers.ts causes infinite recursion when a worklet captures a React Context, because Context.Provider === Context (self-reference by design in React).
Steps to reproduce
git clone https://github.com/thomasttvo/reanimated-jest-extractinputs-repro
cd reanimated-jest-extractinputs-repro
yarn install
yarn testSnack or a link to a repository
https://github.com/thomasttvo/reanimated-jest-extractinputs-repro
Reanimated version
3.19.4
React Native version
0.83.1
Platforms
iOS, Android (Jest environment)
JavaScript runtime
Hermes
Workflow
React Native CLI
Architecture
New Architecture
Build type
Debug
Device
iOS Simulator
Minimal Reproduction
import {createContext} from 'react';
import {useAnimatedStyle} from 'react-native-reanimated';
const MyContext = createContext(false);
function Component() {
const animatedStyle = useAnimatedStyle(() => {
const _ctx = MyContext; // Captured in worklet closure
return { opacity: 1 };
});
}Root Cause
React.createContext()returns{ Provider: <self>, Consumer: {...} }whereContext.Provider === Context- Referencing Context inside a worklet → Babel plugin captures it in
__closure extractInputsrecursively iterates plain objects to find SharedValues- Context is a plain object (prototype === Object.prototype)
extractInputsiteratesContext.Providerwhich equalsContext→ infinite recursion
Why This Doesn't Happen in Production
In production, makeShareableCloneRecursiveNative has cycle detection via caching.
In Jest, makeShareableCloneRecursiveWeb returns values unchanged (no cycle detection), so extractInputs receives raw objects with circular references.
Error
RangeError: Maximum call stack size exceeded
at extractInputs (node_modules/react-native-reanimated/src/mappers.ts:140:25)
at extractInputs (node_modules/react-native-reanimated/src/mappers.ts:155:20)
at extractInputs ...
Related Issues
- Snapshots excessively large or failing with
Invalid string length#6645 - Similar issue withjestInlineStylecausing infinite snapshots (fixed viafilterOutAnimatedStyles) - This issue is in
extractInputs, which was NOT addressed by that fix
Suggested Fix
Add cycle detection (WeakSet) to extractInputs:
function extractInputs(
inputs: unknown,
resultArray: MapperExtractedInputs,
visited = new WeakSet()
): MapperExtractedInputs {
if (Array.isArray(inputs)) {
for (const input of inputs) {
if (input) {
extractInputs(input, resultArray, visited);
}
}
} else if (isSharedValue(inputs)) {
resultArray.push(inputs);
} else if (Object.getPrototypeOf(inputs) === Object.prototype) {
if (visited.has(inputs as object)) {
return resultArray; // Skip already-visited objects
}
visited.add(inputs as object);
for (const element of Object.values(inputs as Record<string, unknown>)) {
if (element) {
extractInputs(element, resultArray, visited);
}
}
}
return resultArray;
}Metadata
Metadata
Assignees
Labels
Platform: AndroidThis issue is specific to AndroidThis issue is specific to AndroidPlatform: iOSThis issue is specific to iOSThis issue is specific to iOSRepro providedA reproduction with a snippet of code, snack or repo is providedA reproduction with a snippet of code, snack or repo is provided