[React Native] Fix for view config registrations (#16821)
Ricky committed
Sep 18, 2019 at 15:31 UTC
6ecfa90eb0d9bc369d0e6930bc02d59dfba770d1
3 files changed
+51
-2
packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativeViewConfigRegistry.js
+4
-1
@@ -101,10 +101,13 @@ exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
101
: '',
102
);
103
}
104
- viewConfigCallbacks.set(name, null);
104
viewConfig = callback();
105
processEventTypes(viewConfig);
106
viewConfigs.set(name, viewConfig);
107
+
108
+ // Clear the callback after the config is set so that
109
+ // we don't mask any errors during registration.
110
+ viewConfigCallbacks.set(name, null);
111
} else {
112
viewConfig = viewConfigs.get(name);
113
}
packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js
+43
@@ -75,6 +75,49 @@ beforeEach(() => {
75
.ReactNativeViewConfigRegistry.register;
76
});
77
78
+it('fails to register the same event name with different types', () => {
79
+ const InvalidEvents = createReactNativeComponentClass('InvalidEvents', () => {
80
+ if (!__DEV__) {
81
+ // Simulate a registration error in prod.
82
+ throw new Error('Event cannot be both direct and bubbling: topChange');
83
+ }
84
+
85
+ // This view config has the same bubbling and direct event name
86
+ // which will fail to register in developement.
87
+ return {
88
+ uiViewClassName: 'InvalidEvents',
89
+ validAttributes: {
90
+ onChange: true,
91
+ },
92
+ bubblingEventTypes: {
93
+ topChange: {
94
+ phasedRegistrationNames: {
95
+ bubbled: 'onChange',
96
+ captured: 'onChangeCapture',
97
+ },
98
+ },
99
+ },
100
+ directEventTypes: {
101
+ topChange: {
102
+ registrationName: 'onChange',
103
+ },
104
+ },
105
+ };
106
+ });
107
+
108
+ // The first time this renders,
109
+ // we attempt to register the view config and fail.
110
+ expect(() => ReactNative.render(<InvalidEvents />, 1)).toThrow(
111
+ 'Event cannot be both direct and bubbling: topChange',
112
+ );
113
+
114
+ // Continue to re-register the config and
115
+ // fail so that we don't mask the above failure.
116
+ expect(() => ReactNative.render(<InvalidEvents />, 1)).toThrow(
117
+ 'Event cannot be both direct and bubbling: topChange',
118
+ );
119
+});
120
+
121
it('fails if unknown/unsupported event types are dispatched', () => {
122
expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1);
123
const EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js
+4
-1
@@ -98,10 +98,13 @@ exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
98
: '',
99
);
100
}
101
- viewConfigCallbacks.set(name, null);
101
viewConfig = callback();
102
processEventTypes(viewConfig);
103
viewConfigs.set(name, viewConfig);
104
+
105
+ // Clear the callback after the config is set so that
106
+ // we don't mask any errors during registration.
107
+ viewConfigCallbacks.set(name, null);
108
} else {
109
viewConfig = viewConfigs.get(name);
110
}