Skip to content

Commit 6ecfa90

Browse files
authored
[React Native] Fix for view config registrations (#16821)
1 parent 18cb590 commit 6ecfa90

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativeViewConfigRegistry.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,13 @@ exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
101101
: '',
102102
);
103103
}
104-
viewConfigCallbacks.set(name, null);
105104
viewConfig = callback();
106105
processEventTypes(viewConfig);
107106
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);
108111
} else {
109112
viewConfig = viewConfigs.get(name);
110113
}

packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,49 @@ beforeEach(() => {
7575
.ReactNativeViewConfigRegistry.register;
7676
});
7777

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+
78121
it('fails if unknown/unsupported event types are dispatched', () => {
79122
expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1);
80123
const EventEmitter = RCTEventEmitter.register.mock.calls[0][0];

scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,13 @@ exports.get = function(name: string): ReactNativeBaseComponentViewConfig<> {
9898
: '',
9999
);
100100
}
101-
viewConfigCallbacks.set(name, null);
102101
viewConfig = callback();
103102
processEventTypes(viewConfig);
104103
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);
105108
} else {
106109
viewConfig = viewConfigs.get(name);
107110
}

0 commit comments

Comments
 (0)