Skip to content

ViewScreenshotService reflects for removed ReactViewGroup.dispatchOverflowDraw on RN 0.86 → snapshots skip overflow clipping + logcat spam #3975

Description

@tausifcreates

Summary

On React Native 0.86, makeImageFromView / view snapshots log a NoSuchMethodException for every ReactViewGroup drawn and silently skip overflow (rounded-corner / overflow: hidden) clipping in the captured image.

ViewScreenshotService.drawChildren() invokes ReactViewGroup.dispatchOverflowDraw(Canvas) by reflection:

// packages/skia/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java
Method method = ReactViewGroup.class.getDeclaredMethod("dispatchOverflowDraw", cArg);
method.setAccessible(true);
method.invoke(group, canvas);

RN 0.86 rewrote ReactViewGroup in Kotlin and removed dispatchOverflowDraw. Overflow clipping moved into the dispatchDraw override:

// react-native 0.86: ReactViewGroup.kt
override fun dispatchDraw(canvas: Canvas) {
  if (_overflow != Overflow.VISIBLE || getTag(R.id.filter) != null) {
    clipToPaddingBox(this, canvas)   // BackgroundStyleApplicator.clipToPaddingBox(view, canvas)
  }
  super.dispatchDraw(canvas)
}

So on RN 0.86 the reflected method no longer exists, every call throws NoSuchMethodException, is caught, and logs:

E SkiaScreenshot: couldn't invoke dispatchOverflowDraw() on ReactViewGroup
  java.lang.NoSuchMethodException: com.facebook.react.views.view.<ReactViewGroup>.dispatchOverflowDraw [class android.graphics.Canvas]

It is caught (no crash), but (1) snapshots of views with overflow: hidden / border-radius no longer get the overflow clip applied, and (2) it fires once per ReactViewGroup per capture (observed ~25+ times on a single screen), throwing an exception each time on the draw path.

This reproduces on both minified (R8) and non-minified builds: it is a straight API removal, not obfuscation. (dispatchOverflowDraw is absent from RN 0.86's ReactViewGroup.kt and from a non-R8 dex, confirmed with dexdump.)

Environment

  • @shopify/react-native-skia: 2.9.1-next.1 (also present in 2.10.0, identical reflection)
  • react-native: 0.86.0
  • Expo SDK 57, New Architecture + Hermes, Android

Repro

Snapshot (makeImageFromView) any <View style={{ overflow: 'hidden', borderRadius: N }}> subtree on RN 0.86 and watch logcat (adb logcat | grep SkiaScreenshot).

Suggested fix

Keep the existing reflection for RN < 0.86, and fall back to RN 0.86+'s BackgroundStyleApplicator.clipToPaddingBox(view, canvas) (also reflected, to avoid a hard version dependency) gated on the public getOverflow() state. clipToPaddingBox is @JvmStatic public fun clipToPaddingBox(view: View, canvas: Canvas) in RN 0.86.

if (group instanceof ReactViewGroup) {
    try {
        // RN < 0.86: private ReactViewGroup.dispatchOverflowDraw(Canvas)
        Method method = ReactViewGroup.class.getDeclaredMethod("dispatchOverflowDraw", Canvas.class);
        method.setAccessible(true);
        method.invoke(group, canvas);
    } catch (NoSuchMethodException removedInRn086) {
        // RN >= 0.86: overflow clipping moved to dispatchDraw() ->
        // BackgroundStyleApplicator.clipToPaddingBox(view, canvas), gated on overflow != visible.
        try {
            String overflow = ((ReactViewGroup) group).getOverflow();
            if (overflow != null && !overflow.equals("visible")) {
                Class<?> bsa = Class.forName("com.facebook.react.uimanager.BackgroundStyleApplicator");
                Method clip = bsa.getMethod("clipToPaddingBox", View.class, Canvas.class);
                clip.invoke(null, group, canvas); // @JvmStatic -> null receiver
            }
        } catch (Exception e) {
            Log.e(TAG, "couldn't apply RN>=0.86 overflow clip (clipToPaddingBox)", e);
        }
    } catch (Exception e) {
        Log.e(TAG, "couldn't invoke dispatchOverflowDraw() on ReactViewGroup", e);
    }
}

Caching the resolved Method/Class in static fields (instead of resolving per child, per frame) would also remove the current per-draw reflection + exception cost.

Note: this is an untested suggestion pointing at the RN 0.86 replacement API, the exact binding should be validated against the maintainers' supported RN version matrix, since BackgroundStyleApplicator is an internal RN API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions