From b0982971c6ed6be98b99db902a38dfdf5e9b8b37 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 14 Dec 2023 08:52:54 -0800 Subject: [PATCH] Improve setting props of the Interop Layer (#41942) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/41942 Previously, every time a component was updated, we were passing all the props to the interoperated component. With this change, we are going to only pass the props that are changed. As a safety feature, if the new codepath is not able to detect the type of the prop properly, it will fall back to the previous behavior. ## Changelog: [Internal] - Only pass props to the interoperated component when they changes Reviewed By: sammy-SC Differential Revision: D51755764 fbshipit-source-id: 0185d2cceeab2a1e45b87d5a1e82ab06e00aa82d --- .../RCTLegacyViewManagerInteropCoordinator.mm | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm index afaea7424dd5f6..5424099638f374 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm +++ b/packages/react-native/ReactCommon/react/renderer/components/legacyviewmanagerinterop/RCTLegacyViewManagerInteropCoordinator.mm @@ -41,6 +41,8 @@ @implementation RCTLegacyViewManagerInteropCoordinator { */ NSMutableArray> *_moduleMethods; NSMutableDictionary> *_moduleMethodsByName; + + NSDictionary *_oldProps; } - (instancetype)initWithComponentData:(RCTComponentData *)componentData @@ -100,11 +102,13 @@ - (void)setProps:(const folly::dynamic &)props forView:(UIView *)view { if (props.isObject()) { NSDictionary *convertedProps = convertFollyDynamicToId(props); - [_componentData setProps:convertedProps forView:view]; + NSDictionary *diffedProps = [self _diffProps:convertedProps]; + [_componentData setProps:diffedProps forView:view]; if ([view respondsToSelector:@selector(didSetProps:)]) { - [view performSelector:@selector(didSetProps:) withObject:[convertedProps allKeys]]; + [view performSelector:@selector(didSetProps:) withObject:[diffedProps allKeys]]; } + _oldProps = convertedProps; } } @@ -248,4 +252,85 @@ - (void)_lookupModuleMethodsIfNecessary } } +- (NSDictionary *)_diffProps:(NSDictionary *)newProps +{ + NSMutableDictionary *diffedProps = [NSMutableDictionary new]; + + [newProps enumerateKeysAndObjectsUsingBlock:^(NSString *key, id newProp, __unused BOOL *stop) { + id oldProp = _oldProps[key]; + if ([self _prop:newProp isDifferentFrom:oldProp]) { + diffedProps[key] = newProp; + } + }]; + + return diffedProps; +} + +- (BOOL)_prop:(id)oldProp isDifferentFrom:(id)newProp +{ + // Check for JSON types. + // JSON types can be of: + // * number + // * bool + // * String + // * Array + // * Objects => Dictionaries in ObjectiveC + // * Null + + // Check for NULL + BOOL bothNil = !oldProp && !newProp; + if (bothNil) { + return NO; + } + + BOOL onlyOneNil = (oldProp && !newProp) || (!oldProp && newProp); + if (onlyOneNil) { + return YES; + } + + if ([self _propIsSameNumber:oldProp second:newProp]) { + // Boolean should be captured by NSNumber + return NO; + } + + if ([self _propIsSameString:oldProp second:newProp]) { + return NO; + } + + if ([self _propIsSameArray:oldProp second:newProp]) { + return NO; + } + + if ([self _propIsSameObject:oldProp second:newProp]) { + return NO; + } + + // Previous behavior, fallback to YES + return YES; +} + +- (BOOL)_propIsSameNumber:(id)first second:(id)second +{ + return [first isKindOfClass:[NSNumber class]] && [second isKindOfClass:[NSNumber class]] && + [(NSNumber *)first isEqualToNumber:(NSNumber *)second]; +} + +- (BOOL)_propIsSameString:(id)first second:(id)second +{ + return [first isKindOfClass:[NSString class]] && [second isKindOfClass:[NSString class]] && + [(NSString *)first isEqualToString:(NSString *)second]; +} + +- (BOOL)_propIsSameArray:(id)first second:(id)second +{ + return [first isKindOfClass:[NSArray class]] && [second isKindOfClass:[NSArray class]] && + [(NSArray *)first isEqualToArray:(NSArray *)second]; +} + +- (BOOL)_propIsSameObject:(id)first second:(id)second +{ + return [first isKindOfClass:[NSDictionary class]] && [second isKindOfClass:[NSDictionary class]] && + [(NSDictionary *)first isEqualToDictionary:(NSDictionary *)second]; +} + @end