Skip to content

Commit 7d2be2c

Browse files
authored
Merge pull request #8400 from sebmarkbage/fibercontainerchildren
[Fiber] Update root children using appendChild/insertBefore/removeChild
2 parents 8791325 + ea34204 commit 7d2be2c

File tree

8 files changed

+153
-129
lines changed

8 files changed

+153
-129
lines changed

scripts/fiber/tests-failing.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,3 @@ src/renderers/shared/stack/reconciler/__tests__/refs-test.js
120120

121121
src/test/__tests__/ReactTestUtils-test.js
122122
* traverses children in the correct order
123-
* should support injected wrapper components as DOM components

scripts/fiber/tests-passing.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,7 @@ src/test/__tests__/ReactTestUtils-test.js
15601560
* can scryRenderedDOMComponentsWithClass with TextComponent
15611561
* can scryRenderedDOMComponentsWithClass with className contains \n
15621562
* can scryRenderedDOMComponentsWithClass with multiple classes
1563+
* should support injected wrapper components as DOM components
15631564
* should change the value of an input field
15641565
* should change the value of an input field in a component
15651566
* should throw when attempting to use ReactTestUtils.Simulate with shallow rendering

src/renderers/dom/fiber/ReactDOMFiber.js

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
'use strict';
1414

1515
import type { Fiber } from 'ReactFiber';
16-
import type { HostChildren } from 'ReactFiberReconciler';
1716
import type { ReactNodeList } from 'ReactTypes';
1817

1918
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
@@ -39,6 +38,8 @@ var {
3938
} = ReactDOMFiberComponent;
4039
var { precacheFiberNode } = ReactDOMComponentTree;
4140

41+
const DOCUMENT_NODE = 9;
42+
4243
ReactDOMInjection.inject();
4344
ReactControlledComponent.injection.injectFiberControlledHostComponent(
4445
ReactDOMFiberComponent
@@ -54,23 +55,6 @@ type Props = { className ?: string };
5455
type Instance = Element;
5556
type TextInstance = Text;
5657

57-
function recursivelyAppendChildren(parent : Element, child : HostChildren<Instance | TextInstance>) {
58-
if (!child) {
59-
return;
60-
}
61-
/* $FlowFixMe: Element and Text should have this property. */
62-
if (child.nodeType === 1 || child.nodeType === 3) {
63-
/* $FlowFixMe: Refinement issue. I don't know how to express different. */
64-
parent.appendChild(child);
65-
} else {
66-
/* As a result of the refinement issue this type isn't known. */
67-
let node : any = child;
68-
do {
69-
recursivelyAppendChildren(parent, node.output);
70-
} while (node = node.sibling);
71-
}
72-
}
73-
7458
let eventsEnabled : ?boolean = null;
7559
let selectionInformation : ?mixed = null;
7660

@@ -89,27 +73,28 @@ var DOMRenderer = ReactFiberReconciler({
8973
eventsEnabled = null;
9074
},
9175

92-
updateContainer(container : Container, children : HostChildren<Instance | TextInstance>) : void {
93-
// TODO: Containers should update similarly to other parents.
94-
container.innerHTML = '';
95-
recursivelyAppendChildren(container, children);
96-
},
97-
9876
createInstance(
9977
type : string,
10078
props : Props,
101-
children : HostChildren<Instance | TextInstance>,
10279
internalInstanceHandle : Object
10380
) : Instance {
104-
const root = document.body; // HACK
81+
const root = document.documentElement; // HACK
10582

10683
const domElement : Instance = createElement(type, props, root);
10784
precacheFiberNode(internalInstanceHandle, domElement);
108-
recursivelyAppendChildren(domElement, children);
109-
setInitialProperties(domElement, type, props, root);
11085
return domElement;
11186
},
11287

88+
appendInitialChild(parentInstance : Instance, child : Instance | TextInstance) : void {
89+
parentInstance.appendChild(child);
90+
},
91+
92+
finalizeInitialChildren(domElement : Instance, type : string, props : Props) : void {
93+
const root = document.documentElement; // HACK
94+
95+
setInitialProperties(domElement, type, props, root);
96+
},
97+
11398
prepareUpdate(
11499
domElement : Instance,
115100
oldProps : Props,
@@ -125,7 +110,7 @@ var DOMRenderer = ReactFiberReconciler({
125110
internalInstanceHandle : Object
126111
) : void {
127112
var type = domElement.tagName.toLowerCase(); // HACK
128-
var root = document.body; // HACK
113+
var root = document.documentElement; // HACK
129114
// Update the internal instance handle so that we know which props are
130115
// the current ones.
131116
precacheFiberNode(internalInstanceHandle, domElement);
@@ -142,19 +127,19 @@ var DOMRenderer = ReactFiberReconciler({
142127
textInstance.nodeValue = newText;
143128
},
144129

145-
appendChild(parentInstance : Instance, child : Instance | TextInstance) : void {
130+
appendChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
146131
parentInstance.appendChild(child);
147132
},
148133

149134
insertBefore(
150-
parentInstance : Instance,
135+
parentInstance : Instance | Container,
151136
child : Instance | TextInstance,
152137
beforeChild : Instance | TextInstance
153138
) : void {
154139
parentInstance.insertBefore(child, beforeChild);
155140
},
156141

157-
removeChild(parentInstance : Instance, child : Instance | TextInstance) : void {
142+
removeChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
158143
parentInstance.removeChild(child);
159144
},
160145

@@ -180,9 +165,15 @@ function warnAboutUnstableUse() {
180165
warned = true;
181166
}
182167

183-
function renderSubtreeIntoContainer(parentComponent : ?ReactComponent<any, any, any>, element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
168+
function renderSubtreeIntoContainer(parentComponent : ?ReactComponent<any, any, any>, element : ReactElement<any>, containerNode : DOMContainerElement | Document, callback: ?Function) {
169+
let container : DOMContainerElement =
170+
containerNode.nodeType === DOCUMENT_NODE ? (containerNode : any).documentElement : (containerNode : any);
184171
let root;
185172
if (!container._reactRootContainer) {
173+
// First clear any existing content.
174+
while (container.lastChild) {
175+
container.removeChild(container.lastChild);
176+
}
186177
root = container._reactRootContainer = DOMRenderer.mountContainer(element, container, parentComponent, callback);
187178
} else {
188179
DOMRenderer.updateContainer(element, root = container._reactRootContainer, parentComponent, callback);
@@ -197,12 +188,12 @@ var ReactDOM = {
197188
return renderSubtreeIntoContainer(null, element, container, callback);
198189
},
199190

200-
unstable_renderSubtreeIntoContainer(parentComponent : ReactComponent<any, any, any>, element : ReactElement<any>, container : DOMContainerElement, callback: ?Function) {
191+
unstable_renderSubtreeIntoContainer(parentComponent : ReactComponent<any, any, any>, element : ReactElement<any>, containerNode : DOMContainerElement | Document, callback: ?Function) {
201192
invariant(
202193
parentComponent != null && ReactInstanceMap.has(parentComponent),
203194
'parentComponent must be a valid React Component'
204195
);
205-
return renderSubtreeIntoContainer(parentComponent, element, container, callback);
196+
return renderSubtreeIntoContainer(parentComponent, element, containerNode, callback);
206197
},
207198

208199
unmountComponentAtNode(container : DOMContainerElement) {

src/renderers/noop/ReactNoop.js

Lines changed: 24 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import type { Fiber } from 'ReactFiber';
2323
import type { UpdateQueue } from 'ReactFiberUpdateQueue';
24-
import type { HostChildren } from 'ReactFiberReconciler';
2524

2625
var ReactFiberReconciler = require('ReactFiberReconciler');
2726
var ReactInstanceMap = require('ReactInstanceMap');
@@ -32,59 +31,35 @@ var {
3231
var scheduledAnimationCallback = null;
3332
var scheduledDeferredCallback = null;
3433

35-
const TERMINAL_TAG = 99;
36-
const TEXT_TAG = 98;
37-
3834
type Container = { rootID: string, children: Array<Instance | TextInstance> };
3935
type Props = { prop: any };
40-
type Instance = { tag: 99, type: string, id: number, children: Array<Instance | TextInstance>, prop: any };
41-
type TextInstance = { tag: 98, text: string };
36+
type Instance = {| type: string, id: number, children: Array<Instance | TextInstance>, prop: any |};
37+
type TextInstance = {| text: string, id: number |};
4238

4339
var instanceCounter = 0;
4440

45-
function recursivelyAppendChildren(
46-
flatArray : Array<Instance | TextInstance>,
47-
child : HostChildren<Instance | TextInstance>
48-
) {
49-
if (!child) {
50-
return;
51-
}
52-
if (child.tag === TERMINAL_TAG || child.tag === TEXT_TAG) {
53-
flatArray.push(child);
54-
} else {
55-
let node = child;
56-
do {
57-
recursivelyAppendChildren(flatArray, node.output);
58-
} while (node = node.sibling);
59-
}
60-
}
61-
62-
function flattenChildren(children : HostChildren<Instance | TextInstance>) {
63-
const flatArray = [];
64-
recursivelyAppendChildren(flatArray, children);
65-
return flatArray;
66-
}
67-
6841
var NoopRenderer = ReactFiberReconciler({
6942

70-
updateContainer(containerInfo : Container, children : HostChildren<Instance | TextInstance>) : void {
71-
containerInfo.children = flattenChildren(children);
72-
},
73-
74-
createInstance(type : string, props : Props, children : HostChildren<Instance | TextInstance>) : Instance {
43+
createInstance(type : string, props : Props) : Instance {
7544
const inst = {
76-
tag: TERMINAL_TAG,
7745
id: instanceCounter++,
7846
type: type,
79-
children: flattenChildren(children),
47+
children: [],
8048
prop: props.prop,
8149
};
8250
// Hide from unit tests
83-
Object.defineProperty(inst, 'tag', { value: inst.tag, enumerable: false });
8451
Object.defineProperty(inst, 'id', { value: inst.id, enumerable: false });
8552
return inst;
8653
},
8754

55+
appendInitialChild(parentInstance : Instance, child : Instance | TextInstance) : void {
56+
parentInstance.children.push(child);
57+
},
58+
59+
finalizeInitialChildren(domElement : Instance, type : string, props : Props) : void {
60+
// Noop
61+
},
62+
8863
prepareUpdate(instance : Instance, oldProps : Props, newProps : Props) : boolean {
8964
return true;
9065
},
@@ -94,17 +69,17 @@ var NoopRenderer = ReactFiberReconciler({
9469
},
9570

9671
createTextInstance(text : string) : TextInstance {
97-
var inst = { tag: TEXT_TAG, text : text };
72+
var inst = { text : text, id: instanceCounter++ };
9873
// Hide from unit tests
99-
Object.defineProperty(inst, 'tag', { value: inst.tag, enumerable: false });
74+
Object.defineProperty(inst, 'id', { value: inst.id, enumerable: false });
10075
return inst;
10176
},
10277

10378
commitTextUpdate(textInstance : TextInstance, oldText : string, newText : string) : void {
10479
textInstance.text = newText;
10580
},
10681

107-
appendChild(parentInstance : Instance, child : Instance | TextInstance) : void {
82+
appendChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
10883
const index = parentInstance.children.indexOf(child);
10984
if (index !== -1) {
11085
parentInstance.children.splice(index, 1);
@@ -113,7 +88,7 @@ var NoopRenderer = ReactFiberReconciler({
11388
},
11489

11590
insertBefore(
116-
parentInstance : Instance,
91+
parentInstance : Instance | Container,
11792
child : Instance | TextInstance,
11893
beforeChild : Instance | TextInstance
11994
) : void {
@@ -128,7 +103,7 @@ var NoopRenderer = ReactFiberReconciler({
128103
parentInstance.children.splice(beforeIndex, 0, child);
129104
},
130105

131-
removeChild(parentInstance : Instance, child : Instance | TextInstance) : void {
106+
removeChild(parentInstance : Instance | Container, child : Instance | TextInstance) : void {
132107
const index = parentInstance.children.indexOf(child);
133108
if (index === -1) {
134109
throw new Error('This child does not exist.');
@@ -213,7 +188,7 @@ var ReactNoop = {
213188
}
214189
// Unsound duck typing.
215190
const component = (componentOrElement : any);
216-
if (component.tag === TERMINAL_TAG || component.tag === TEXT_TAG) {
191+
if (typeof component.id === 'number') {
217192
return component;
218193
}
219194
const inst = ReactInstanceMap.get(component);
@@ -278,10 +253,13 @@ var ReactNoop = {
278253
function logHostInstances(children: Array<Instance | TextInstance>, depth) {
279254
for (var i = 0; i < children.length; i++) {
280255
var child = children[i];
281-
if (child.tag === TEXT_TAG) {
282-
log(' '.repeat(depth) + '- ' + child.text);
256+
var indent = ' '.repeat(depth);
257+
if (typeof child.text === 'string') {
258+
log(indent + '- ' + child.text);
283259
} else {
284-
log(' '.repeat(depth) + '- ' + child.type + '#' + child.id);
260+
// $FlowFixMe - The child should've been refined now.
261+
log(indent + '- ' + child.type + '#' + child.id);
262+
// $FlowFixMe - The child should've been refined now.
285263
logHostInstances(child.children, depth + 1);
286264
}
287265
}

src/renderers/shared/fiber/ReactFiberBeginWork.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,24 @@ module.exports = function<T, P, I, TI, C>(
300300
}
301301

302302
function updatePortalComponent(current, workInProgress) {
303-
reconcileChildren(current, workInProgress, workInProgress.pendingProps);
303+
const priorityLevel = workInProgress.pendingWorkPriority;
304+
const nextChildren = workInProgress.pendingProps;
305+
if (!current) {
306+
// Portals are special because we don't append the children during mount
307+
// but at commit. Therefore we need to track insertions which the normal
308+
// flow doesn't do during mount. This doesn't happen at the root because
309+
// the root always starts with a "current" with a null child.
310+
// TODO: Consider unifying this with how the root works.
311+
workInProgress.child = reconcileChildFibersInPlace(
312+
workInProgress,
313+
workInProgress.child,
314+
nextChildren,
315+
priorityLevel
316+
);
317+
markChildAsProgressed(current, workInProgress, priorityLevel);
318+
} else {
319+
reconcileChildren(current, workInProgress, nextChildren);
320+
}
304321
}
305322

306323
/*

0 commit comments

Comments
 (0)