Files
react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager
Ramanpreet Nara 4b9e4fa1ef Codemod: Make Android native ViewConfigs inherit parents' bubbling/direct events
Summary:
# Problem
1. Static ViewConfigs on **both platforms** contain their parent component's inherited bubbling/direct events (and props).
2. On Android, native ViewConfigs for child components **do not** inherit bubbling/direct events from their parent. (They do inherit the props, however).

# Cause

How child components inherit props from their parent component on Android:
1. A ViewManager's native props are calculated by [calling ViewManager.getNativeProps()](https://www.internalfb.com/code/fbsource/[5769b6d6ca123b2bed31dc2bc6bc8e4701581891]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java?lines=139)
2. Which [calls into ViewManagerPropertyUpdater.getNativeProps()](https://www.internalfb.com/code/fbsource/[11f0031c5e83d4d8903112d7d720b58981d3613f]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java?lines=278-280)
3. Which [calls into a code-generated $$PropsSetter object](https://www.internalfb.com/code/fbsource/[11f0031c5e83d4d8903112d7d720b58981d3613f]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.java?lines=73-74%2C112)
4. The ReactProp annotation processor [code-generates a $$PropsSetter object](https://www.internalfb.com/code/fbsource/[cbc8ca6036219069ad52fb6aec66488b7a06a879]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java?lines=265) by [visiting the ViewManager’s *entire class hierarchy* in search of ReactProp annotations](https://www.internalfb.com/code/fbsource/[cbc8ca6036219069ad52fb6aec66488b7a06a879]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java?lines=203-230).

Why child components don't inherit direct/bubbling events from their parents:
1. When we get the bubbling/direct events for a component on Android, we just call into the child ViewManager, which didn't forward its parent's props (until this diff):

https://www.internalfb.com/code/fbsource/[5769b6d6ca123b2bed31dc2bc6bc8e4701581891]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java?lines=113%2C122

# Fix

Codemod all components to manually forward the bubbling/direct events from their parents.

Why Not:
- Leads to a lot of boilerplate code.
- Feels like a bandaid solution.
- Doesn’t scale to open source. There’s more process when authoring new components.
- Are thre more idiomatic alternative solutions? (See alternatives considered).

Why:
- It’s a bandaid solution, yes. And it doesn’t scale well to other components, true. But, we’re only bloating deprecated APIs. Long term, we’re going to kill off getExportedCustomBubblingEventTypeConstants() and getExportedCustomDirectEventTypeConstants().
- Our goal is to just unblock Static ViewConfigs. This is the simplest/safest/least intrusive way to accomplish our goal.

# FAQ
**If child components don't contain their parents bubbling/direct events, how can they can respond to their parent's bubbling/direct events?**
- Bubbling/direct events are stored in a [global map](https://www.internalfb.com/code/fbsource/[2de1e1d59f6e0316868a6c4d9bca5fe673210106]/xplat/js/react-native-github/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js?lines=20-34). Once *one* component registers a bubbling/direct event, all components [can respond to that event](https://www.internalfb.com/code/fbsource/[2de1e1d59f6e0316868a6c4d9bca5fe673210106]/xplat/js/react-native-github/Libraries/Renderer/implementations/ReactFabric-dev.js?lines=2439-2440).

**How does this component prop/event inheriting work on iOS?**
1. On iOS, ViewConfigs have a baseViewConfig property.
2. All components at least have baseViewConfig = RCTViewManager.
3. To create the native ViewConfig for the component, JavaScript runs this loop:

https://www.internalfb.com/code/fbsource/[058bc6c4976d4cebb442dd2675a2a0570a214403]/xplat/js/react-native-github/Libraries/ReactNative/getNativeComponentAttributes.js?lines=42-61

# Alternative Solutions
***Solution 1:** Make Android components leverage baseViewConfig, like iOS.*

Why Not:
- baseViewConfig leads to unnecessary round trips from JS → Native (see [this TODO](https://www.internalfb.com/code/fbsource/[a88c9751494f1ee863a76238b532fca2b134032d]/xplat/js/react-native-github/Libraries/ReactNative/getNativeComponentAttributes.js?lines=34-35) that tells us we should avoid this on iOS).

***Solution 2:** In [UIManagerModuleConstantsHelper](https://www.internalfb.com/code/fbsource/[6717cba1e0db71777cf11dcf7b861b171bfd0c84]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java?lines=105-145), which generates the native ViewConfig for Android components, use Java reflection APIs to visit the class hierarchy of all ViewManagers when collecting bubbling/direct events.*

Complications:
- **Challenging to Implement**: Have to rely on advanced Java reflection APIs. You can’t just do getSuperclass().getDeclaredMethod(...).invoke(object) to invoke an overridden method.
- **Challenging to Ship**: Would lead to a penalties when creating native view configs: either performance, or memory. This negatively impacts VR and legacy React Native android.

***Solution 3:** Create a deprecated ReactProp-like annotation (e.g: ReactEvent) but for declaring bubbling/direct events in Java ViewManagers for zero runtime cost event declaration.*

Details:
- Legacy React Native infra will be code-modded to newer ReactEvent annotation infra.
- The ReactEvent annotation processor will navigate the ViewManager’s class hierarchy at build-time to generate a class that returns the ViewManager’s bubbling/direct events.
- UIManagerModuleConstantsHelper will call into this class to get the bubbling/direct events.
- **Aside:** The component codegen can also generate these annotations. This way, all you need to do is hook up your ViewManagers to codegen to guarantee that your native component exports the right bubbling/direct events to JavaScript.

Why Not:
- This is a lot of throwaway work for just making the SVC === NVC check pass, which we're only doing to unblock the SVC migration.

Why is this throwaway work?
- Java ViewManagers don’t do anything special in native with the Bubbling/Direct events. They declare the Bubbling/Direct events for JavaScript consumption. If JavaScript is the source of truth, then there’s no value in sending these Bubbling/Direct events to Java ViewManagers.

Changelog: [Internal]

Reviewed By: JoshuaGross

Differential Revision: D33303418

fbshipit-source-id: 8d99fe80f83244443406bcfdc6cfea43b26f9c75
2022-01-04 16:04:16 -08:00
..
2021-03-24 03:52:31 -07:00