Sample App: https://rnplay.org/apps/L_-nNQ
I am using a RootView component to detect device rotation, but when animating an absolutely positioned child View in the class hierarchy, the RootView is getting redundant layout events.
<RootView onLayout={onDeviceRotation}>
<Animated.View>
</Animated.View>
</RootView>
On Android, onDeviceRotation gets a layout event for each frame (when the child view changes). On iOS, onDeviceRotation is called only on initial layout.
Digging into the source code, I found that:
- On iOS, if a view is dirtied but if its frame doesn't change, then onLayout doesn't happen.
- On Android, if a view is dirtied but if its frame doesn't change, onLayout DOES happen.

The relevant code is below.
iOS Side
RCTUIManager dispatches onLayout event for views that received new frames.
See: https://github.com/facebook/react-native/blob/02578df4f731b411b7b0be5f5dd83bd7233d2800/React/Modules/RCTUIManager.m#L550-L567
for (RCTShadowView *shadowView in viewsWithNewFrames) {
...
if (shadowView.onLayout) {
CGRect frame = shadowView.frame;
shadowView.onLayout(@{
@"layout": @{
@"x": @(frame.origin.x),
@"y": @(frame.origin.y),
@"width": @(frame.size.width),
@"height": @(frame.size.height),
},
});
}
RCTShadowNodes checks if frame changed, it not, don't add the view to viewsWithNewFrames, thus preventing onLayout event to get dispatched.
See: https://github.com/facebook/react-native/blob/02578df4f731b411b7b0be5f5dd83bd7233d2800/React/Views/RCTShadowView.m#L152-L155
if (!CGRectEqualToRect(frame, _frame)) {
_frame = frame;
[viewsWithNewFrame addObject:self];
}
Android Side
Android always dispatch an event if the layout is dirtied. In our case, the child view dirties the parent. We can do something similar as iOS, by checking if the frame changed before dispatching an event.
See: https://github.com/facebook/react-native/blob/02578df4f731b411b7b0be5f5dd83bd7233d2800/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java#L729-L744
cssNode.dispatchUpdates(
absoluteX,
absoluteY,
mOperationsQueue,
mNativeViewHierarchyOptimizer);
// notify JS about layout event if requested
if (cssNode.shouldNotifyOnLayout()) {
eventDispatcher.dispatchEvent(
OnLayoutEvent.obtain(
tag,
cssNode.getScreenX(),
cssNode.getScreenY(),
cssNode.getScreenWidth(),
cssNode.getScreenHeight()));
}
Specifically, we can change dispatchUpdates to return false if frame didn't change, and avoid dispatching the onLayout event.
Sample App: https://rnplay.org/apps/L_-nNQ
I am using a
RootViewcomponent to detect device rotation, but when animating an absolutely positioned child View in the class hierarchy, the RootView is getting redundant layout events.On Android,
onDeviceRotationgets a layout event for each frame (when the child view changes). On iOS,onDeviceRotationis called only on initial layout.Digging into the source code, I found that:
The relevant code is below.
iOS Side
RCTUIManager dispatches onLayout event for views that received new frames.
See: https://github.com/facebook/react-native/blob/02578df4f731b411b7b0be5f5dd83bd7233d2800/React/Modules/RCTUIManager.m#L550-L567
RCTShadowNodes checks if frame changed, it not, don't add the view to
viewsWithNewFrames, thus preventing onLayout event to get dispatched.See: https://github.com/facebook/react-native/blob/02578df4f731b411b7b0be5f5dd83bd7233d2800/React/Views/RCTShadowView.m#L152-L155
Android Side
Android always dispatch an event if the layout is dirtied. In our case, the child view dirties the parent. We can do something similar as iOS, by checking if the frame changed before dispatching an event.
See: https://github.com/facebook/react-native/blob/02578df4f731b411b7b0be5f5dd83bd7233d2800/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java#L729-L744
Specifically, we can change
dispatchUpdatesto return false if frame didn't change, and avoid dispatching the onLayout event.