Skip to content

Commit a8175ef

Browse files
authored
Fix button with react component doesn't get unmounted (wix#5457)
* Fix button with react component doesn't get unmounted * Test e2e only on iOS
1 parent 8f2463e commit a8175ef

13 files changed

+83
-6
lines changed

e2e/Options.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ describe('Options', () => {
8888
await expect(elementByLabel('Styling Options')).toBeVisible();
8989
});
9090

91+
it(':ios: Reseting buttons should unmount button react view', async () => {
92+
await elementById(TestIDs.SHOW_LIFECYCLE_BTN).tap();
93+
await elementById(TestIDs.RESET_BUTTONS).tap();
94+
await expect(elementByLabel('Button component unmounted')).toBeVisible();
95+
});
96+
9197
xit('hides topBar onScroll down and shows it on scroll up', async () => {
9298
await elementById(TestIDs.PUSH_OPTIONS_BUTTON).tap();
9399
await elementById(TestIDs.SCROLLVIEW_SCREEN_BUTTON).tap();

lib/ios/RNNNavigationButtons.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ -(RNNUIBarButtonItem*)buildButton: (NSDictionary*)dictionary defaultStyle:(RNNBu
103103
componentOptions.name = [[Text alloc] initWithValue:component[@"name"]];
104104

105105
RNNReactView *view = [_componentRegistry createComponentIfNotExists:componentOptions parentComponentId:self.viewController.layoutInfo.componentId reactViewReadyBlock:nil];
106-
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withCustomView:view];
106+
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withCustomView:view componentRegistry:_componentRegistry];
107107
} else if (iconImage) {
108108
barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withIcon:iconImage];
109109
} else if (title) {

lib/ios/RNNReactComponentRegistry.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
- (void)removeComponent:(NSString *)componentId;
1313

14+
- (void)removeChildComponent:(NSString *)childId;
15+
1416
- (void)clearComponentsForParentId:(NSString *)parentComponentId;
1517

1618
- (void)clear;

lib/ios/RNNReactComponentRegistry.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ - (void)removeComponent:(NSString *)componentId {
4848
}
4949
}
5050

51+
- (void)removeChildComponent:(NSString *)childId {
52+
NSMutableDictionary* parent;
53+
while ((parent = _componentStore.objectEnumerator.nextObject)) {
54+
if ([parent objectForKey:childId]) {
55+
[parent removeObjectForKey:childId];
56+
return;
57+
}
58+
}
59+
}
60+
5161
- (void)clear {
5262
[_componentStore removeAllObjects];
5363
}

lib/ios/RNNReactView.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
1212

1313
- (void)setAlignment:(NSString *)alignment inFrame:(CGRect)frame;
1414

15+
- (NSString *)componentId;
16+
1517
@end

lib/ios/RNNReactView.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@ - (void)contentDidAppear:(NSNotification *)notification {
2222

2323
RNNReactView* appearedView = notification.object;
2424

25-
if (_reactViewReadyBlock && [appearedView.appProperties[@"componentId"] isEqual:self.appProperties[@"componentId"]]) {
25+
if (_reactViewReadyBlock && [appearedView.appProperties[@"componentId"] isEqual:self.componentId]) {
2626
_reactViewReadyBlock();
2727
_reactViewReadyBlock = nil;
2828
[[NSNotificationCenter defaultCenter] removeObserver:self];
2929
}
3030
}
3131

32+
- (NSString *)componentId {
33+
return self.appProperties[@"componentId"];
34+
}
35+
3236
- (void)setRootViewDidChangeIntrinsicSize:(void (^)(CGSize))rootViewDidChangeIntrinsicSize {
3337
_rootViewDidChangeIntrinsicSize = rootViewDidChangeIntrinsicSize;
3438
self.delegate = self;

lib/ios/RNNUIBarButtonItem.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
#import <Foundation/Foundation.h>
22
#import <React/RCTRootView.h>
33
#import <React/RCTRootViewDelegate.h>
4+
#import "RNNReactComponentRegistry.h"
45

56
@interface RNNUIBarButtonItem : UIBarButtonItem <RCTRootViewDelegate>
67

78
@property (nonatomic, strong) NSString* buttonId;
89

910
-(instancetype)init:(NSString*)buttonId withIcon:(UIImage*)iconImage;
1011
-(instancetype)init:(NSString*)buttonId withTitle:(NSString*)title;
11-
-(instancetype)init:(NSString*)buttonId withCustomView:(RCTRootView*)reactView;
12+
-(instancetype)init:(NSString*)buttonId withCustomView:(RCTRootView *)reactView componentRegistry:(RNNReactComponentRegistry *)componentRegistry;
1213
-(instancetype)init:(NSString*)buttonId withSystemItem:(NSString*)systemItemName;
1314

1415
@end

lib/ios/RNNUIBarButtonItem.m

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ @interface RNNUIBarButtonItem ()
77

88
@property (nonatomic, strong) NSLayoutConstraint *widthConstraint;
99
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
10+
@property (nonatomic, weak) RNNReactComponentRegistry *componentRegistry;
1011

1112
@end
1213

@@ -28,9 +29,10 @@ -(instancetype)init:(NSString*)buttonId withTitle:(NSString*)title {
2829
return self;
2930
}
3031

31-
-(instancetype)init:(NSString*)buttonId withCustomView:(RCTRootView *)reactView {
32+
-(instancetype)init:(NSString*)buttonId withCustomView:(RCTRootView *)reactView componentRegistry:(RNNReactComponentRegistry *)componentRegistry {
3233
self = [super initWithCustomView:reactView];
3334

35+
self.componentRegistry = componentRegistry;
3436
reactView.sizeFlexibility = RCTRootViewSizeFlexibilityWidthAndHeight;
3537
reactView.delegate = self;
3638
reactView.backgroundColor = [UIColor clearColor];
@@ -76,4 +78,11 @@ - (void)onButtonPressed {
7678
afterDelay:0];
7779
}
7880

81+
- (void)dealloc {
82+
if ([self.customView isKindOfClass:[RNNReactView class]]) {
83+
RNNReactView* customView = self.customView;
84+
[self.componentRegistry removeChildComponent:customView.componentId];
85+
}
86+
}
87+
7988
@end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const RoundedButton = require('./RoundedButton');
2+
3+
class LifecycleButton extends RoundedButton {
4+
componentWillUnmount() {
5+
alert('Button component unmounted');
6+
}
7+
}
8+
9+
module.exports = LifecycleButton;

playground/src/screens/OptionsScreen.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const React = require('react');
2-
const { Component } = require('react');
2+
const {Component} = require('react');
33
const Root = require('../components/Root');
44
const Button = require('../components/Button')
55
const Navigation = require('../services/Navigation');
@@ -16,7 +16,9 @@ const {
1616
PUSH_BTN,
1717
HIDE_TOPBAR_DEFAULT_OPTIONS,
1818
SHOW_YELLOW_BOX_BTN,
19-
SET_REACT_TITLE_VIEW
19+
SET_REACT_TITLE_VIEW,
20+
RESET_BUTTONS,
21+
SHOW_LIFECYCLE_BTN
2022
} = require('../testIDs');
2123

2224
class Options extends Component {
@@ -69,10 +71,36 @@ class Options extends Component {
6971
<Button label='Set React Title View' testID={SET_REACT_TITLE_VIEW} onPress={this.setReactTitleView} />
7072
<Button label='Show Yellow Box' testID={SHOW_YELLOW_BOX_BTN} onPress={() => console.warn('Yellow Box')} />
7173
<Button label='StatusBar' onPress={this.statusBarScreen} />
74+
<Button label='Show Lifecycle button' testID={SHOW_LIFECYCLE_BTN} onPress={this.showLifecycleButton} />
75+
<Button label='Remove all buttons' testID={RESET_BUTTONS} onPress={this.resetButtons} />
7276
</Root>
7377
);
7478
}
7579

80+
showLifecycleButton = () => Navigation.mergeOptions(this, {
81+
topBar: {
82+
rightButtons: [
83+
{
84+
id: 'ROUND',
85+
testID: ROUND_BUTTON,
86+
component: {
87+
name: Screens.LifecycleButton,
88+
passProps: {
89+
title: 'Two'
90+
}
91+
}
92+
}
93+
]
94+
}
95+
});
96+
97+
resetButtons = () => Navigation.mergeOptions(this, {
98+
topBar: {
99+
rightButtons: [],
100+
leftButtons: []
101+
}
102+
});
103+
76104
changeTitle = () => Navigation.mergeOptions(this, {
77105
topBar: {
78106
title: {

0 commit comments

Comments
 (0)