Skip to content

Commit 7d1deda

Browse files
sherginfacebook-github-bot
authored andcommitted
New implementation of handling didUpdateReactSubviews and didUpdateReactSubviews events
Summary: Motivation: * Current implementation of `didUpdateReactSubviews` relies on `processUpdatedProperties:parentProperties:` method of RCTShadowView, which we plan to remove. * The existing implementation does not call handlers on unmounted nodes (because they are not part of traversing tree), which is not correct. * The current implementation is tight with RCTComponentData, which is conceptually wrong, it should be a UIManager thing. * The new implementation must be much more performant because of simplicity. (We can measure it only after removing `processUpdatedProperties`.) Reviewed By: mmmulani Differential Revision: D6595780 fbshipit-source-id: a517207c17b5d5db839c9ce99a37136292acf78c
1 parent 4996b9a commit 7d1deda

File tree

6 files changed

+102
-26
lines changed

6 files changed

+102
-26
lines changed

React/Modules/RCTUIManager.m

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ @implementation RCTUIManager
6666
NSMutableDictionary<NSNumber *, RCTShadowView *> *_shadowViewRegistry; // RCT thread only
6767
NSMutableDictionary<NSNumber *, UIView *> *_viewRegistry; // Main thread only
6868

69+
NSMapTable<RCTShadowView *, NSArray<NSString *> *> *_shadowViewsWithUpdatedProps; // UIManager queue only.
70+
NSHashTable<RCTShadowView *> *_shadowViewsWithUpdatedChildren; // UIManager queue only.
71+
6972
// Keyed by viewName
7073
NSDictionary *_componentDataByName;
7174
}
@@ -138,6 +141,9 @@ - (void)setBridge:(RCTBridge *)bridge
138141
_shadowViewRegistry = [NSMutableDictionary new];
139142
_viewRegistry = [NSMutableDictionary new];
140143

144+
_shadowViewsWithUpdatedProps = [NSMapTable weakToStrongObjectsMapTable];
145+
_shadowViewsWithUpdatedChildren = [NSHashTable weakObjectsHashTable];
146+
141147
// Internal resources
142148
_pendingUIBlocks = [NSMutableArray new];
143149
_rootViewTags = [NSMutableSet new];
@@ -839,6 +845,8 @@ - (void)_removeChildren:(NSArray<UIView *> *)children
839845
RCTSetChildren(containerTag, reactTags,
840846
(NSDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry);
841847
}];
848+
849+
[self _shadowViewDidReceiveUpdatedChildren:_shadowViewRegistry[containerTag]];
842850
}
843851

844852
static void RCTSetChildren(NSNumber *containerTag,
@@ -879,6 +887,8 @@ static void RCTSetChildren(NSNumber *containerTag,
879887
removeAtIndices:removeAtIndices
880888
registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry];
881889
}];
890+
891+
[self _shadowViewDidReceiveUpdatedChildren:_shadowViewRegistry[containerTag]];
882892
}
883893

884894
- (void)_manageChildren:(NSNumber *)containerTag
@@ -972,6 +982,13 @@ - (void)_manageChildren:(NSNumber *)containerTag
972982
#endif
973983
}
974984
});
985+
986+
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
987+
UIView *view = viewRegistry[reactTag];
988+
[componentData setProps:props forView:view];
989+
}];
990+
991+
[self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
975992
}
976993

977994
RCT_EXPORT_METHOD(updateView:(nonnull NSNumber *)reactTag
@@ -986,6 +1003,8 @@ - (void)_manageChildren:(NSNumber *)containerTag
9861003
UIView *view = viewRegistry[reactTag];
9871004
[componentData setProps:props forView:view];
9881005
}];
1006+
1007+
[self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
9891008
}
9901009

9911010
- (void)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag
@@ -1067,6 +1086,9 @@ - (void)_layoutAndMount
10671086
[self addUIBlock:uiBlock];
10681087
}
10691088

1089+
[self _dispatchPropsDidChangeEvents];
1090+
[self _dispatchChildrenDidChangeEvents];
1091+
10701092
[_observerCoordinator uiManagerWillPerformLayout:self];
10711093

10721094
// Perform layout
@@ -1135,6 +1157,75 @@ - (void)setNeedsLayout
11351157
}
11361158
}
11371159

1160+
- (void)_shadowView:(RCTShadowView *)shadowView didReceiveUpdatedProps:(NSArray<NSString *> *)props
1161+
{
1162+
// We collect a set with changed `shadowViews` and its changed props,
1163+
// so we have to maintain this collection properly.
1164+
NSArray<NSString *> *previousProps;
1165+
if ((previousProps = [_shadowViewsWithUpdatedProps objectForKey:shadowView])) {
1166+
// Merging already registred changed props and new ones.
1167+
NSMutableSet *set = [NSMutableSet setWithArray:previousProps];
1168+
[set addObjectsFromArray:props];
1169+
props = [set allObjects];
1170+
}
1171+
1172+
[_shadowViewsWithUpdatedProps setObject:props forKey:shadowView];
1173+
}
1174+
1175+
- (void)_shadowViewDidReceiveUpdatedChildren:(RCTShadowView *)shadowView
1176+
{
1177+
[_shadowViewsWithUpdatedChildren addObject:shadowView];
1178+
}
1179+
1180+
- (void)_dispatchChildrenDidChangeEvents
1181+
{
1182+
if (_shadowViewsWithUpdatedChildren.count == 0) {
1183+
return;
1184+
}
1185+
1186+
NSHashTable<RCTShadowView *> *shadowViews = _shadowViewsWithUpdatedChildren;
1187+
_shadowViewsWithUpdatedChildren = [NSHashTable weakObjectsHashTable];
1188+
1189+
NSMutableArray *tags = [NSMutableArray arrayWithCapacity:shadowViews.count];
1190+
1191+
for (RCTShadowView *shadowView in shadowViews) {
1192+
[shadowView didUpdateReactSubviews];
1193+
[tags addObject:shadowView.reactTag];
1194+
}
1195+
1196+
[self addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
1197+
for (NSNumber *tag in tags) {
1198+
UIView<RCTComponent> *view = viewRegistry[tag];
1199+
[view didUpdateReactSubviews];
1200+
}
1201+
}];
1202+
}
1203+
1204+
- (void)_dispatchPropsDidChangeEvents
1205+
{
1206+
if (_shadowViewsWithUpdatedProps.count == 0) {
1207+
return;
1208+
}
1209+
1210+
NSMapTable<RCTShadowView *, NSArray<NSString *> *> *shadowViews = _shadowViewsWithUpdatedProps;
1211+
_shadowViewsWithUpdatedProps = [NSMapTable weakToStrongObjectsMapTable];
1212+
1213+
NSMapTable<NSNumber *, NSArray<NSString *> *> *tags = [NSMapTable strongToStrongObjectsMapTable];
1214+
1215+
for (RCTShadowView *shadowView in shadowViews) {
1216+
NSArray<NSString *> *props = [shadowViews objectForKey:shadowView];
1217+
[shadowView didSetProps:props];
1218+
[tags setObject:props forKey:shadowView.reactTag];
1219+
}
1220+
1221+
[self addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
1222+
for (NSNumber *tag in tags) {
1223+
UIView<RCTComponent> *view = viewRegistry[tag];
1224+
[view didSetProps:[tags objectForKey:tag]];
1225+
}
1226+
}];
1227+
}
1228+
11381229
RCT_EXPORT_METHOD(measure:(nonnull NSNumber *)reactTag
11391230
callback:(RCTResponseSenderBlock)callback)
11401231
{

React/Views/RCTComponent.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ typedef void (^RCTBubblingEventBlock)(NSDictionary *body);
3636
// View/ShadowView is a root view
3737
- (BOOL)isReactRootView;
3838

39-
@optional
40-
4139
/**
4240
* Called each time props have been set.
4341
* Not all props have to be set - React can set only changed ones.

React/Views/RCTComponentData.m

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,6 @@ - (void)setProps:(NSDictionary<NSString *, id> *)props forView:(id<RCTComponent>
355355
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
356356
[self propBlockForKey:key isShadowView:NO](view, json);
357357
}];
358-
359-
if ([view respondsToSelector:@selector(didSetProps:)]) {
360-
[view didSetProps:[props allKeys]];
361-
}
362358
}
363359

364360
- (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowView *)shadowView
@@ -370,10 +366,6 @@ - (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowV
370366
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
371367
[self propBlockForKey:key isShadowView:YES](shadowView, json);
372368
}];
373-
374-
if ([shadowView respondsToSelector:@selector(didSetProps:)]) {
375-
[shadowView didSetProps:[props allKeys]];
376-
}
377369
}
378370

379371
- (NSDictionary<NSString *, id> *)viewConfig

React/Views/RCTShadowView.m

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ @implementation RCTShadowView
4141
BOOL _recomputePadding;
4242
BOOL _recomputeMargin;
4343
BOOL _recomputeBorder;
44-
BOOL _didUpdateSubviews;
4544
YGValue _paddingMetaProps[META_PROP_COUNT];
4645
YGValue _marginMetaProps[META_PROP_COUNT];
4746
YGValue _borderMetaProps[META_PROP_COUNT];
@@ -263,19 +262,6 @@ - (void)applyLayoutToChildren:(YGNodeRef)node
263262
- (NSDictionary<NSString *, id> *)processUpdatedProperties:(NSMutableSet<RCTApplierBlock> *)applierBlocks
264263
parentProperties:(NSDictionary<NSString *, id> *)parentProperties
265264
{
266-
// TODO: we always refresh all propagated properties when propagation is
267-
// dirtied, but really we should track which properties have changed and
268-
// only update those.
269-
270-
if (_didUpdateSubviews) {
271-
_didUpdateSubviews = NO;
272-
[self didUpdateReactSubviews];
273-
[applierBlocks addObject:^(NSDictionary<NSNumber *, UIView *> *viewRegistry) {
274-
UIView *view = viewRegistry[self->_reactTag];
275-
[view didUpdateReactSubviews];
276-
}];
277-
}
278-
279265
return parentProperties;
280266
}
281267

@@ -435,7 +421,6 @@ - (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
435421
YGNodeInsertChild(_yogaNode, subview.yogaNode, (uint32_t)atIndex);
436422
}
437423
subview->_superview = self;
438-
_didUpdateSubviews = YES;
439424
[self dirtyText];
440425
[self dirtyPropagation];
441426
}
@@ -444,7 +429,6 @@ - (void)removeReactSubview:(RCTShadowView *)subview
444429
{
445430
[subview dirtyText];
446431
[subview dirtyPropagation];
447-
_didUpdateSubviews = YES;
448432
subview->_superview = nil;
449433
[_reactSubviews removeObject:subview];
450434
if (![self isYogaLeafNode]) {

React/Views/UIView+React.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@
6060
*/
6161
- (void)didUpdateReactSubviews;
6262

63+
/**
64+
* Called each time props have been set.
65+
* The default implementation does nothing.
66+
*/
67+
- (void)didSetProps:(NSArray<NSString *> *)changedProps;
68+
6369
/**
6470
* Used by the UIIManager to set the view frame.
6571
* May be overriden to disable animation, etc.

React/Views/UIView+React.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ - (void)didUpdateReactSubviews
172172
}
173173
}
174174

175+
- (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
176+
{
177+
// The default implementation does nothing.
178+
}
179+
175180
- (void)reactSetFrame:(CGRect)frame
176181
{
177182
// These frames are in terms of anchorPoint = topLeft, but internally the

0 commit comments

Comments
 (0)