Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,17 @@ bool sliceChangedChildShadowNodeViewPairsRecursively(
continue;
}

if (oldChildShadowNode.getTag() != newChildShadowNode.getTag() ||
// A child whose tag or order index changed can still be listed if it is a
// leaf that doesn't form a view, like the raw text of a `Text` whose
// string changed: it has no mount index, so it doesn't shift the views
// around it, and both lists are sorted by order index at the end.
bool isReplaced =
oldChildShadowNode.getTag() != newChildShadowNode.getTag() ||
oldChildShadowNode.getOrderIndex() !=
newChildShadowNode.getOrderIndex()) {
newChildShadowNode.getOrderIndex();
if (isReplaced &&
(!oldChildShadowNode.getChildren().empty() ||
!newChildShadowNode.getChildren().empty())) {
return false;
}

Expand All @@ -282,7 +290,12 @@ bool sliceChangedChildShadowNodeViewPairsRecursively(
newLayoutOffset);
bool isStatic = newChildSlice.shadowView.layoutMetrics.positionType ==
PositionType::Static;
if (oldChildSlice.isConcreteView != newChildSlice.isConcreteView ||
if (isReplaced) {
if (oldChildSlice.isConcreteView || newChildSlice.isConcreteView) {
return false;
}
} else if (
oldChildSlice.isConcreteView != newChildSlice.isConcreteView ||
oldChildSlice.areChildrenFlattened !=
newChildSlice.areChildrenFlattened ||
(oldChildSlice.shadowView.layoutMetrics.positionType ==
Expand All @@ -303,7 +316,7 @@ bool sliceChangedChildShadowNodeViewPairsRecursively(
newChildSlice,
newChildShadowNode);

if (!newChildSlice.areChildrenFlattened) {
if (isReplaced || !newChildSlice.areChildrenFlattened) {
continue;
}
if (isStatic) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {HostInstance} from 'react-native';
import * as Fantom from '@react-native/fantom';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {ScrollView, View} from 'react-native';
import {ScrollView, Text, View} from 'react-native';

describe('ViewFlattening', () => {
/**
Expand Down Expand Up @@ -593,6 +593,52 @@ describe('unchanged children', () => {
]);
});

test('a text whose string changes next to unchanged children is updated', () => {
const root = Fantom.createRoot();
const render = (value: number) =>
Fantom.runTask(() => {
root.render(
<View nativeID="parent">
<Unchanged nativeID="A" />
<Text nativeID="label">Value {value}</Text>
<Unchanged nativeID="B" />
</View>,
);
});

render(1);
root.takeMountingManagerLogs();

// React gives the changed string a new raw text node, which doesn't form
// a view, so only the paragraph is updated.
render(2);
expect(root.takeMountingManagerLogs()).toEqual([
'Update {type: "Paragraph", nativeID: "label"}',
]);
});

test('an empty view that is remounted next to unchanged children causes no mutations', () => {
const root = Fantom.createRoot();
const render = (version: number) =>
Fantom.runTask(() => {
root.render(
<View nativeID="parent">
<Unchanged nativeID="A" />
<View key={version} />
<Unchanged nativeID="B" />
</View>,
);
});

render(1);
root.takeMountingManagerLogs();

// The new key gives the empty view a new tag. It is flattened and has no
// children, so it doesn't form a view and nothing is mounted for it.
render(2);
expect(root.takeMountingManagerLogs()).toEqual([]);
});

test('a child added next to unchanged children is inserted', () => {
const root = Fantom.createRoot();
const render = (showAdded: boolean) =>
Expand Down
Loading