From 1c2aa2e14655681503e1e368ca2effbc45b2f990 Mon Sep 17 00:00:00 2001 From: Gregory Moskaliuk Date: Sun, 18 Oct 2026 12:43:40 +0200 Subject: [PATCH] Skip layer invalidation when an iOS View only changes opacity An opacity change set `needsInvalidateLayer`, so `finalizeUpdates:` ran the whole `invalidateLayer`: shadow path, borders, and the background, box-shadow, outline and mask layers. `invalidateLayer` reads the opacity only to combine it with `filter`, and the new opacity is already applied to the layer in `updateProps:`, so the invalidation is now requested only when the view has a filter. Native-driver and Reanimated opacity animations update every animated view on every frame, so each styled view paid the full invalidation per frame. Co-Authored-By: Claude Opus 5.5 --- .../Mounting/ComponentViews/View/RCTViewComponentView.mm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 582557336d61..296d57079296 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -355,7 +355,10 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & if (oldViewProps.opacity != newViewProps.opacity && ![_propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN containsObject:@"opacity"]) { self.layer.opacity = (float)newViewProps.opacity; - needsInvalidateLayer = YES; + // `invalidateLayer` only reads the opacity to combine it with `filter`. + if (!newViewProps.filter.empty()) { + needsInvalidateLayer = YES; + } } // Disable `removeClippedSubviews` when Fabric View Culling is enabled.