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
This commit is contained in:
Riccardo Cipolleschi
2023-12-14 08:52:54 -08:00
committed by Facebook GitHub Bot
parent 201d2d1fde
commit b0982971c6
@@ -41,6 +41,8 @@ using namespace facebook::react;
*/
NSMutableArray<id<RCTBridgeMethod>> *_moduleMethods;
NSMutableDictionary<NSString *, id<RCTBridgeMethod>> *_moduleMethodsByName;
NSDictionary<NSString *, id> *_oldProps;
}
- (instancetype)initWithComponentData:(RCTComponentData *)componentData
@@ -100,11 +102,13 @@ using namespace facebook::react;
{
if (props.isObject()) {
NSDictionary<NSString *, id> *convertedProps = convertFollyDynamicToId(props);
[_componentData setProps:convertedProps forView:view];
NSDictionary<NSString *, id> *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 @@ using namespace facebook::react;
}
}
- (NSDictionary<NSString *, id> *)_diffProps:(NSDictionary<NSString *, id> *)newProps
{
NSMutableDictionary<NSString *, id> *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