Child Fiber
Sebastian Markbage committed
May 23, 2016 at 16:18 UTC
7c8a090994ab74018c8e938e47cce411548b3a53
8 files changed
+301
-77
src/renderers/noop/__tests__/ReactNoop-test.js
+10
-7
@@ -20,7 +20,7 @@ describe('ReactComponent', function() {
20
ReactNoop = require('ReactNoop');
21
});
22
23
- it('should render a simple component', function() {
23
+ /*it('should render a simple component', function() {
24
25
function Bar() {
26
return <div>Hello World</div>;
@@ -33,24 +33,27 @@ describe('ReactComponent', function() {
33
ReactNoop.render(<Foo />);
34
ReactNoop.flush();
35
36
- });
36
+ });*/
37
38
it('should render a simple component, in steps if needed', function() {
39
40
function Bar() {
41
- return <div>Hello World</div>;
41
+ return <span><div>Hello World</div></span>;
42
}
43
44
function Foo() {
45
- return <Bar isBar={true} />;
45
+ return [
46
+ <Bar isBar={true} />,
47
+ <Bar isBar={true} />,
48
+ ];
49
}
50
51
ReactNoop.render(<Foo />);
49
- // console.log('Nothing done');
52
+ console.log('Nothing done');
53
ReactNoop.flushLowPri(7);
51
- // console.log('Yield');
54
+ console.log('Yield');
55
ReactNoop.flushLowPri(50);
53
- // console.log('Done');
56
+ console.log('Done');
57
});
58
59
src/renderers/shared/fiber/ReactChildFiber.js
new
+86
@@ -0,0 +1,86 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactChildFiber
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+import type { Fiber } from 'ReactFiber';
16
+
17
+var ReactElement = require('ReactElement');
18
+
19
+var ReactFiber = require('ReactFiber');
20
+
21
+type ReactNode = ReactElement | ReactFragment | ReactText;
22
+
23
+type ReactFragment = Iterable<ReactNode | ReactEmpty>;
24
+
25
+type ReactNodeList = ReactNode | ReactEmpty;
26
+
27
+type ReactText = string | number;
28
+
29
+type ReactEmpty = null | void | boolean;
30
+
31
+function createSubsequentChild(parent : Fiber, previousSibling : Fiber, newChildren) : Fiber {
32
+ if (typeof newChildren !== 'object' || newChildren === null) {
33
+ return previousSibling;
34
+ }
35
+
36
+ if (ReactElement.isValidElement(newChildren)) {
37
+ var element = (newChildren : ReactElement);
38
+ var child = ReactFiber.createFiberFromElement(element);
39
+ previousSibling.sibling = child;
40
+ child.parent = parent;
41
+ return child;
42
+ }
43
+
44
+ if (Array.isArray(newChildren)) {
45
+ let prev : Fiber = previousSibling;
46
+ for (var i = 0; i < newChildren.length; i++) {
47
+ prev = createSubsequentChild(parent, prev, newChildren[i]);
48
+ }
49
+ return prev;
50
+ } else {
51
+ return previousSibling;
52
+ }
53
+}
54
+
55
+function createFirstChild(parent, newChildren) {
56
+ if (typeof newChildren !== 'object' || newChildren === null) {
57
+ parent.child = null;
58
+ return null;
59
+ }
60
+
61
+ if (ReactElement.isValidElement(newChildren)) {
62
+ var element = (newChildren : ReactElement);
63
+ var child = ReactFiber.createFiberFromElement(element);
64
+ parent.child = child;
65
+ child.parent = parent;
66
+ return child;
67
+ }
68
+
69
+ if (Array.isArray(newChildren)) {
70
+ var prev : ?Fiber = null;
71
+ for (var i = 0; i < newChildren.length; i++) {
72
+ if (prev == null) {
73
+ prev = createFirstChild(parent, newChildren[i]);
74
+ } else {
75
+ prev = createSubsequentChild(parent, prev, newChildren[i]);
76
+ }
77
+ }
78
+ } else {
79
+ parent.child = null;
80
+ return null;
81
+ }
82
+}
83
+
84
+exports.reconcileChildFibers = function(parent : Fiber, newChildren : ReactNodeList) : void {
85
+ createFirstChild(parent, newChildren);
86
+};
src/renderers/shared/fiber/ReactFiber.js
+28
-7
@@ -12,6 +12,13 @@
12
13
'use strict';
14
15
+var ReactTypesOfWork = require('ReactTypesOfWork');
16
+var {
17
+ IndeterminateComponent,
18
+ ClassComponent,
19
+ HostComponent,
20
+} = ReactTypesOfWork;
21
+
22
type StateNode = {};
23
type EffectHandler = () => void;
24
type EffectTag = number;
@@ -27,16 +34,13 @@ export type Fiber = {
34
input: ?Object,
35
output: ?Object,
36
30
- handler: EffectHandler,
31
- handlerTag: EffectTag,
32
-
37
hasPendingChanges: bool,
38
39
stateNode: StateNode,
40
41
};
42
39
-module.exports = function(tag : number) : Fiber {
43
+var createFiber = function(tag : number, handlerTag : number) : Fiber {
44
return {
45
46
tag: tag,
@@ -48,12 +52,29 @@ module.exports = function(tag : number) : Fiber {
52
input: null,
53
output: null,
54
51
- handler: function() {},
52
- handlerTag: 0,
53
-
55
hasPendingChanges: true,
56
57
stateNode: {},
58
59
};
60
};
61
+
62
+function shouldConstruct(Component) {
63
+ return !!(Component.prototype && Component.prototype.isReactComponent);
64
+}
65
+
66
+exports.createFiberFromElement = function(element : ReactElement) {
67
+ let fiber;
68
+ if (typeof element.type === 'function') {
69
+ fiber = shouldConstruct(element.type) ?
70
+ createFiber(ClassComponent, 0) :
71
+ createFiber(IndeterminateComponent, 0);
72
+ } else if (typeof element.type === 'string') {
73
+ fiber = createFiber(HostComponent, 1);
74
+ } else {
75
+ throw new Error('Unknown component type: ' + typeof element.type);
76
+ }
77
+
78
+ fiber.input = element;
79
+ return fiber;
80
+};
src/renderers/shared/fiber/ReactFiberBeginWork.js
new
+97
@@ -0,0 +1,97 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactFiberBeginWork
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+import type { Fiber } from 'ReactFiber';
16
+
17
+var ReactChildFiber = require('ReactChildFiber');
18
+var ReactTypesOfWork = require('ReactTypesOfWork');
19
+var {
20
+ IndeterminateComponent,
21
+ FunctionalComponent,
22
+ ClassComponent,
23
+ HostComponent,
24
+} = ReactTypesOfWork;
25
+
26
+function getElement(unitOfWork) : ReactElement {
27
+ var element = unitOfWork.input;
28
+ if (!element) {
29
+ throw new Error('Should be resolved by now');
30
+ }
31
+ return (element : ReactElement);
32
+}
33
+
34
+function updateFunctionalComponent(unitOfWork) {
35
+ var element = getElement(unitOfWork);
36
+ var fn = element.type;
37
+ var props = element.props;
38
+ console.log('perform work on:', fn.name);
39
+ var nextChildren = fn(props);
40
+
41
+ ReactChildFiber.reconcileChildFibers(
42
+ unitOfWork,
43
+ nextChildren
44
+ );
45
+}
46
+
47
+function updateHostComponent(unitOfWork) {
48
+ var element = getElement(unitOfWork);
49
+ console.log('host component', element.type);
50
+
51
+ var nextChildren = element.props.children;
52
+ ReactChildFiber.reconcileChildFibers(
53
+ unitOfWork,
54
+ nextChildren
55
+ );
56
+}
57
+
58
+function mountIndeterminateComponent(unitOfWork) {
59
+ var element = getElement(unitOfWork);
60
+ var fn = element.type;
61
+ var props = element.props;
62
+ var value = fn(props);
63
+ if (typeof value === 'object' && value && typeof value.render === 'function') {
64
+ console.log('performed work on class:', fn.name);
65
+ // Proceed under the assumption that this is a class instance
66
+ unitOfWork.tag = ClassComponent;
67
+ } else {
68
+ console.log('performed work on fn:', fn.name);
69
+ // Proceed under the assumption that this is a functional component
70
+ unitOfWork.tag = FunctionalComponent;
71
+ }
72
+ ReactChildFiber.reconcileChildFibers(
73
+ unitOfWork,
74
+ value
75
+ );
76
+}
77
+
78
+exports.beginWork = function(unitOfWork : Fiber) : ?Fiber {
79
+ switch (unitOfWork.tag) {
80
+ case IndeterminateComponent:
81
+ mountIndeterminateComponent(unitOfWork);
82
+ break;
83
+ case FunctionalComponent:
84
+ updateFunctionalComponent(unitOfWork);
85
+ break;
86
+ case ClassComponent:
87
+ // $FlowFixMe
88
+ console.log('class component', unitOfWork.input.type.name);
89
+ break;
90
+ case HostComponent:
91
+ updateHostComponent(unitOfWork);
92
+ break;
93
+ default:
94
+ throw new Error('Unknown unit of work tag');
95
+ }
96
+ return unitOfWork.child;
97
+};
src/renderers/shared/fiber/ReactFiberCompleteWork.js
new
+46
@@ -0,0 +1,46 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactFiberCompleteWork
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+import type { Fiber } from 'ReactFiber';
16
+
17
+var ReactChildFiber = require('ReactChildFiber');
18
+var ReactTypesOfWork = require('ReactTypesOfWork');
19
+var {
20
+ IndeterminateComponent,
21
+ FunctionalComponent,
22
+ ClassComponent,
23
+ HostComponent,
24
+} = ReactTypesOfWork;
25
+
26
+exports.completeWork = function(unitOfWork : Fiber) : ?Fiber {
27
+ switch (unitOfWork.tag) {
28
+ case FunctionalComponent:
29
+ // $FlowFixMe
30
+ console.log('/functional component', unitOfWork.input.type.name);
31
+ break;
32
+ case ClassComponent:
33
+ // $FlowFixMe
34
+ console.log('/class component', unitOfWork.input.type.name);
35
+ break;
36
+ case HostComponent:
37
+ // $FlowFixMe
38
+ console.log('/host component', unitOfWork.input.type);
39
+ break;
40
+ case IndeterminateComponent:
41
+ throw new Error('An indeterminate component should have become determinate before completing.');
42
+ default:
43
+ throw new Error('Unknown unit of work tag');
44
+ }
45
+ return null;
46
+};
src/renderers/shared/fiber/ReactFiberFunctionalComponent.js
deleted
-45
@@ -1,45 +0,0 @@
1
-/**
2
- * Copyright 2013-present, Facebook, Inc.
3
- * All rights reserved.
4
- *
5
- * This source code is licensed under the BSD-style license found in the
6
- * LICENSE file in the root directory of this source tree. An additional grant
7
- * of patent rights can be found in the PATENTS file in the same directory.
8
- *
9
- * @providesModule ReactFiberFunctionalComponent
10
- * @flow
11
- */
12
-
13
-'use strict';
14
-
15
-import type { Fiber } from 'ReactFiber';
16
-var createFiber = require('ReactFiber');
17
-
18
-var ReactTypesOfWork = require('ReactTypesOfWork');
19
-var {
20
- FunctionalComponent,
21
-} = ReactTypesOfWork;
22
-
23
-exports.performWork = function(unitOfWork : Fiber) : ?Fiber {
24
- var element = unitOfWork.input;
25
- if (!element) {
26
- throw new Error('Should be resolved by now');
27
- }
28
- var fn = element.type;
29
- var props = element.props;
30
- // console.log('perform work on:', fn.name);
31
- var nextElement = fn(props);
32
-
33
- if (typeof nextElement.type === 'function') {
34
- return exports.createFiber(nextElement);
35
- }
36
- return null;
37
-};
38
-
39
-exports.createFiber = function(element : ReactElement) {
40
- var fiber = createFiber(
41
- FunctionalComponent
42
- );
43
- fiber.input = element;
44
- return fiber;
45
-};
src/renderers/shared/fiber/ReactFiberReconciler.js
+31
-18
@@ -13,14 +13,9 @@
13
'use strict';
14
15
import type { Fiber } from 'ReactFiber';
16
-var ReactFiberFunctionalComponent = require('ReactFiberFunctionalComponent');
17
-
18
-var ReactTypesOfWork = require('ReactTypesOfWork');
19
-var {
20
- FunctionalComponent,
21
- ClassComponent,
22
- HostComponent,
23
-} = ReactTypesOfWork;
16
+var ReactFiber = require('ReactFiber');
17
+var { beginWork } = require('ReactFiberBeginWork');
18
+var { completeWork } = require('ReactFiberCompleteWork');
19
20
type ReactHostElement<T, P> = {
21
type: T,
@@ -54,16 +49,34 @@ module.exports = function<T, P, I>(config : HostConfig<T, P, I>) : Reconciler {
49
50
let nextUnitOfWork : ?Fiber = null;
51
57
- function performUnitOfWork(unit : Fiber) : ?Fiber {
58
- switch (unit.tag) {
59
- case FunctionalComponent:
60
- return ReactFiberFunctionalComponent.performWork(unit);
61
- case ClassComponent:
62
- break;
63
- case HostComponent:
64
- break;
52
+ function completeUnitOfWork(unitOfWork : Fiber) : ?Fiber {
53
+ while (true) {
54
+ var next = completeWork(unitOfWork);
55
+ if (next) {
56
+ // If completing this work spawned new work, do that next.
57
+ return next;
58
+ } else if (unitOfWork.sibling) {
59
+ // If there is more work to do in this parent, do that next.
60
+ return unitOfWork.sibling;
61
+ } else if (unitOfWork.parent) {
62
+ // If there's no more work in this parent. Complete the parent.
63
+ unitOfWork = unitOfWork.parent;
64
+ } else {
65
+ // If we're at the root, there's no more work to do.
66
+ return null;
67
+ }
68
+ }
69
+ }
70
+
71
+ function performUnitOfWork(unitOfWork : Fiber) : ?Fiber {
72
+ var next = beginWork(unitOfWork);
73
+ if (next) {
74
+ // If this spawns new work, do that next.
75
+ return next;
76
+ } else {
77
+ // Otherwise, complete the current work.
78
+ return completeUnitOfWork(unitOfWork);
79
}
66
- return null;
80
}
81
82
function performLowPriWork(deadline : Deadline) {
@@ -100,7 +113,7 @@ module.exports = function<T, P, I>(config : HostConfig<T, P, I>) : Reconciler {
113
114
ensureLowPriIsScheduled();
115
103
- nextUnitOfWork = ReactFiberFunctionalComponent.createFiber(element);
116
+ nextUnitOfWork = ReactFiber.createFiberFromElement(element);
117
118
return {};
119
},
src/renderers/shared/fiber/ReactTypesOfWork.js
+3
@@ -13,9 +13,12 @@
13
'use strict';
14
15
var TypesOfWork = {
16
+ IndeterminateComponent: 0, // Before we know whether it is functional or class
17
FunctionalComponent: 1,
18
ClassComponent: 2,
19
HostComponent: 3,
20
+ CoroutineComponent: 4,
21
+ YieldComponent: 5,
22
};
23
24
module.exports = TypesOfWork;