Skip to content

Commit 9d3ddac

Browse files
committed
Allow new children to notify fragment instances in Fabric
1 parent 0ed6ceb commit 9d3ddac

File tree

2 files changed

+90
-18
lines changed

2 files changed

+90
-18
lines changed

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

+42
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,46 @@ describe('Fabric FragmentRefs', () => {
8080

8181
expect(fragmentRef && fragmentRef._fragmentFiber).toBeTruthy();
8282
});
83+
84+
describe('observers', () => {
85+
// @gate enableFragmentRefs
86+
it('observes children, newly added children', async () => {
87+
let logs = [];
88+
const observer = {
89+
observe: entry => {
90+
// Here we reference internals because we don't need to mock the native observer
91+
// We only need to test that each child node is observed on insertion
92+
logs.push(entry.__internalInstanceHandle.pendingProps.nativeID);
93+
},
94+
};
95+
function Test({showB}) {
96+
const fragmentRef = React.useRef(null);
97+
React.useEffect(() => {
98+
fragmentRef.current.observeUsing(observer);
99+
const lastRefValue = fragmentRef.current;
100+
return () => {
101+
lastRefValue.unobserveUsing(observer);
102+
};
103+
}, []);
104+
return (
105+
<View nativeID="parent">
106+
<React.Fragment ref={fragmentRef}>
107+
<View nativeID="A" />
108+
{showB && <View nativeID="B" />}
109+
</React.Fragment>
110+
</View>
111+
);
112+
}
113+
114+
await act(() => {
115+
ReactFabric.render(<Test showB={false} />, 11, null, true);
116+
});
117+
expect(logs).toEqual(['A']);
118+
logs = [];
119+
await act(() => {
120+
ReactFabric.render(<Test showB={true} />, 11, null, true);
121+
});
122+
expect(logs).toEqual(['B']);
123+
});
124+
});
83125
});

packages/react-reconciler/src/ReactFiberCommitHostEffects.js

+48-18
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,16 @@ export function commitShowHideHostTextInstance(node: Fiber, isHidden: boolean) {
232232

233233
export function commitNewChildToFragmentInstances(
234234
fiber: Fiber,
235-
parentFragmentInstances: Array<FragmentInstanceType>,
235+
parentFragmentInstances: null | Array<FragmentInstanceType>,
236236
): void {
237+
if (
238+
fiber.tag !== HostComponent ||
239+
// Only run fragment insertion effects for initial insertions
240+
fiber.alternate !== null ||
241+
parentFragmentInstances === null
242+
) {
243+
return;
244+
}
237245
for (let i = 0; i < parentFragmentInstances.length; i++) {
238246
const fragmentInstance = parentFragmentInstances[i];
239247
commitNewChildToFragmentInstance(fiber.stateNode, fragmentInstance);
@@ -361,14 +369,7 @@ function insertOrAppendPlacementNodeIntoContainer(
361369
} else {
362370
appendChildToContainer(parent, stateNode);
363371
}
364-
// TODO: Enable HostText for RN
365-
if (
366-
enableFragmentRefs &&
367-
tag === HostComponent &&
368-
// Only run fragment insertion effects for initial insertions
369-
node.alternate === null &&
370-
parentFragmentInstances !== null
371-
) {
372+
if (enableFragmentRefs) {
372373
commitNewChildToFragmentInstances(node, parentFragmentInstances);
373374
}
374375
trackHostMutation();
@@ -426,14 +427,7 @@ function insertOrAppendPlacementNode(
426427
} else {
427428
appendChild(parent, stateNode);
428429
}
429-
// TODO: Enable HostText for RN
430-
if (
431-
enableFragmentRefs &&
432-
tag === HostComponent &&
433-
// Only run fragment insertion effects for initial insertions
434-
node.alternate === null &&
435-
parentFragmentInstances !== null
436-
) {
430+
if (enableFragmentRefs) {
437431
commitNewChildToFragmentInstances(node, parentFragmentInstances);
438432
}
439433
trackHostMutation();
@@ -471,7 +465,7 @@ function insertOrAppendPlacementNode(
471465
}
472466

473467
function commitPlacement(finishedWork: Fiber): void {
474-
if (!supportsMutation) {
468+
if (!supportsMutation && !enableFragmentRefs) {
475469
return;
476470
}
477471

@@ -500,6 +494,13 @@ function commitPlacement(finishedWork: Fiber): void {
500494
'in React. Please file an issue.',
501495
);
502496
}
497+
if (!supportsMutation && enableFragmentRefs) {
498+
appendImmutableNodeToFragmentInstances(
499+
finishedWork,
500+
parentFragmentInstances,
501+
);
502+
return;
503+
}
503504

504505
switch (hostParentFiber.tag) {
505506
case HostSingleton: {
@@ -558,6 +559,35 @@ function commitPlacement(finishedWork: Fiber): void {
558559
}
559560
}
560561

562+
function appendImmutableNodeToFragmentInstances(
563+
finishedWork: Fiber,
564+
parentFragmentInstances: null | Array<FragmentInstanceType>,
565+
): void {
566+
if (!enableFragmentRefs) {
567+
return;
568+
}
569+
const isHost = finishedWork.tag === HostComponent;
570+
if (isHost) {
571+
commitNewChildToFragmentInstances(finishedWork, parentFragmentInstances);
572+
return;
573+
} else if (finishedWork.tag === HostPortal) {
574+
// If the insertion itself is a portal, then we don't want to traverse
575+
// down its children. Instead, we'll get insertions from each child in
576+
// the portal directly.
577+
return;
578+
}
579+
580+
const child = finishedWork.child;
581+
if (child !== null) {
582+
appendImmutableNodeToFragmentInstances(child, parentFragmentInstances);
583+
let sibling = child.sibling;
584+
while (sibling !== null) {
585+
appendImmutableNodeToFragmentInstances(sibling, parentFragmentInstances);
586+
sibling = sibling.sibling;
587+
}
588+
}
589+
}
590+
561591
export function commitHostPlacement(finishedWork: Fiber) {
562592
try {
563593
if (__DEV__) {

0 commit comments

Comments
 (0)