diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
index 5aa26b26340d5..b1f30604a0676 100644
--- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
@@ -3045,19 +3045,19 @@ export function updateFragmentInstanceFiber(
}
export function commitNewChildToFragmentInstance(
- childElement: Instance,
+ childInstance: Instance,
fragmentInstance: FragmentInstanceType,
): void {
const eventListeners = fragmentInstance._eventListeners;
if (eventListeners !== null) {
for (let i = 0; i < eventListeners.length; i++) {
const {type, listener, optionsOrUseCapture} = eventListeners[i];
- childElement.addEventListener(type, listener, optionsOrUseCapture);
+ childInstance.addEventListener(type, listener, optionsOrUseCapture);
}
}
if (fragmentInstance._observers !== null) {
fragmentInstance._observers.forEach(observer => {
- observer.observe(childElement);
+ observer.observe(childInstance);
});
}
}
diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js
index 7a06f157e668f..9a661ee7414b3 100644
--- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js
+++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js
@@ -695,12 +695,17 @@ export function updateFragmentInstanceFiber(
}
export function commitNewChildToFragmentInstance(
- child: Fiber,
+ childInstance: Instance,
fragmentInstance: FragmentInstanceType,
): void {
+ const publicInstance = getPublicInstance(childInstance);
if (fragmentInstance._observers !== null) {
+ if (publicInstance == null) {
+ throw new Error('Expected to find a host node. This is a bug in React.');
+ }
fragmentInstance._observers.forEach(observer => {
- observeChild(child, observer);
+ // $FlowFixMe[incompatible-call] Element types are behind a flag in RN
+ observer.observe(publicInstance);
});
}
}
diff --git a/packages/react-native-renderer/src/__tests__/ReactFabricFragmentRefs-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabricFragmentRefs-test.internal.js
index 725b8d9de694f..ac4bcf36b00b1 100644
--- a/packages/react-native-renderer/src/__tests__/ReactFabricFragmentRefs-test.internal.js
+++ b/packages/react-native-renderer/src/__tests__/ReactFabricFragmentRefs-test.internal.js
@@ -80,4 +80,46 @@ describe('Fabric FragmentRefs', () => {
expect(fragmentRef && fragmentRef._fragmentFiber).toBeTruthy();
});
+
+ describe('observers', () => {
+ // @gate enableFragmentRefs
+ it('observes children, newly added children', async () => {
+ let logs = [];
+ const observer = {
+ observe: entry => {
+ // Here we reference internals because we don't need to mock the native observer
+ // We only need to test that each child node is observed on insertion
+ logs.push(entry.__internalInstanceHandle.pendingProps.nativeID);
+ },
+ };
+ function Test({showB}) {
+ const fragmentRef = React.useRef(null);
+ React.useEffect(() => {
+ fragmentRef.current.observeUsing(observer);
+ const lastRefValue = fragmentRef.current;
+ return () => {
+ lastRefValue.unobserveUsing(observer);
+ };
+ }, []);
+ return (
+
+
+
+ {showB && }
+
+
+ );
+ }
+
+ await act(() => {
+ ReactFabric.render(, 11, null, true);
+ });
+ expect(logs).toEqual(['A']);
+ logs = [];
+ await act(() => {
+ ReactFabric.render(, 11, null, true);
+ });
+ expect(logs).toEqual(['B']);
+ });
+ });
});
diff --git a/packages/react-reconciler/src/ReactFiberCommitHostEffects.js b/packages/react-reconciler/src/ReactFiberCommitHostEffects.js
index 023133f2e9781..5c7ccf3987872 100644
--- a/packages/react-reconciler/src/ReactFiberCommitHostEffects.js
+++ b/packages/react-reconciler/src/ReactFiberCommitHostEffects.js
@@ -255,8 +255,16 @@ export function commitShowHideHostTextInstance(node: Fiber, isHidden: boolean) {
export function commitNewChildToFragmentInstances(
fiber: Fiber,
- parentFragmentInstances: Array,
+ parentFragmentInstances: null | Array,
): void {
+ if (
+ fiber.tag !== HostComponent ||
+ // Only run fragment insertion effects for initial insertions
+ fiber.alternate !== null ||
+ parentFragmentInstances === null
+ ) {
+ return;
+ }
for (let i = 0; i < parentFragmentInstances.length; i++) {
const fragmentInstance = parentFragmentInstances[i];
commitNewChildToFragmentInstance(fiber.stateNode, fragmentInstance);
@@ -384,14 +392,7 @@ function insertOrAppendPlacementNodeIntoContainer(
} else {
appendChildToContainer(parent, stateNode);
}
- // TODO: Enable HostText for RN
- if (
- enableFragmentRefs &&
- tag === HostComponent &&
- // Only run fragment insertion effects for initial insertions
- node.alternate === null &&
- parentFragmentInstances !== null
- ) {
+ if (enableFragmentRefs) {
commitNewChildToFragmentInstances(node, parentFragmentInstances);
}
trackHostMutation();
@@ -449,14 +450,7 @@ function insertOrAppendPlacementNode(
} else {
appendChild(parent, stateNode);
}
- // TODO: Enable HostText for RN
- if (
- enableFragmentRefs &&
- tag === HostComponent &&
- // Only run fragment insertion effects for initial insertions
- node.alternate === null &&
- parentFragmentInstances !== null
- ) {
+ if (enableFragmentRefs) {
commitNewChildToFragmentInstances(node, parentFragmentInstances);
}
trackHostMutation();
@@ -494,10 +488,6 @@ function insertOrAppendPlacementNode(
}
function commitPlacement(finishedWork: Fiber): void {
- if (!supportsMutation) {
- return;
- }
-
// Recursively insert all host nodes into the parent.
let hostParentFiber;
let parentFragmentInstances = null;
@@ -517,6 +507,17 @@ function commitPlacement(finishedWork: Fiber): void {
}
parentFiber = parentFiber.return;
}
+
+ if (!supportsMutation) {
+ if (enableFragmentRefs) {
+ commitImmutablePlacementNodeToFragmentInstances(
+ finishedWork,
+ parentFragmentInstances,
+ );
+ }
+ return;
+ }
+
if (hostParentFiber == null) {
throw new Error(
'Expected to find a host parent. This error is likely caused by a bug ' +
@@ -581,6 +582,41 @@ function commitPlacement(finishedWork: Fiber): void {
}
}
+function commitImmutablePlacementNodeToFragmentInstances(
+ finishedWork: Fiber,
+ parentFragmentInstances: null | Array,
+): void {
+ if (!enableFragmentRefs) {
+ return;
+ }
+ const isHost = finishedWork.tag === HostComponent;
+ if (isHost) {
+ commitNewChildToFragmentInstances(finishedWork, parentFragmentInstances);
+ return;
+ } else if (finishedWork.tag === HostPortal) {
+ // If the insertion itself is a portal, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ return;
+ }
+
+ const child = finishedWork.child;
+ if (child !== null) {
+ commitImmutablePlacementNodeToFragmentInstances(
+ child,
+ parentFragmentInstances,
+ );
+ let sibling = child.sibling;
+ while (sibling !== null) {
+ commitImmutablePlacementNodeToFragmentInstances(
+ sibling,
+ parentFragmentInstances,
+ );
+ sibling = sibling.sibling;
+ }
+ }
+}
+
export function commitHostPlacement(finishedWork: Fiber) {
try {
if (__DEV__) {