@samitouri / QOS-React / commits / 7c8a090994

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;