Queue the event for preallocated but not mounted view to dispatch later

Summary:
This diff fixed an edge case that event dispatching is failed after pre-allocation of a view and before the view is mounted.

When a cached image is loaded, we will dispatch the event to JS immediately. This is could happen after the view is created during pre-allocation phase, when the event emitter is not instantiated yet. In that case, we will see [an error](https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java#L927) and the event will effectively be ignored.

To fix that we introduced a queue in this diff for those events. They will be dispatched in order when the view is mounted and the event emitter is non-null.

Changelog:
[Android][Fixed] - Fixed an edge case that event dispatching is failed after pre-allocation of a view and before the view is mounted.

Reviewed By: mullender

Differential Revision: D36331914

fbshipit-source-id: cd065b0b36978cb5f0aac793d8d16f07a48f0881
This commit is contained in:
Xin Chen
2022-05-13 18:40:17 -07:00
committed by Facebook GitHub Bot
parent 644fe430fd
commit a093fe5f2f
4 changed files with 103 additions and 2 deletions
@@ -59,6 +59,7 @@ import com.facebook.react.fabric.events.FabricEventEmitter;
import com.facebook.react.fabric.mounting.MountItemDispatcher;
import com.facebook.react.fabric.mounting.MountingManager;
import com.facebook.react.fabric.mounting.SurfaceMountingManager;
import com.facebook.react.fabric.mounting.SurfaceMountingManager.ViewEvent;
import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
import com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem;
@@ -923,8 +924,18 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(surfaceId, reactTag);
if (eventEmitter == null) {
// This can happen if the view has disappeared from the screen (because of async events)
FLog.d(TAG, "Unable to invoke event: " + eventName + " for reactTag: " + reactTag);
if (ReactFeatureFlags.enableFabricPendingEventQueue
&& mMountingManager.getViewExists(reactTag)) {
// The view is preallocated and created. However, it hasn't been mounted yet. We will have
// access to the event emitter later when the view is mounted. For now just save the event
// in the view state and trigger it later.
mMountingManager.enqueuePendingEvent(
reactTag,
new ViewEvent(eventName, params, eventCategory, canCoalesceEvent, customCoalesceKey));
} else {
// This can happen if the view has disappeared from the screen (because of async events)
FLog.d(TAG, "Unable to invoke event: " + eventName + " for reactTag: " + reactTag);
}
return;
}