React Fiber Reconciler (#6690)
This is an outline for the new reconciler infrastructure. I created a noop renderer to have something to get started from. I split the reconciler folder into old and new, as well as shared. I put shouldUpdateReactComponent in shared as an example of a utility that can easily be shared between both. I plan on breaking out more utilities like these.
Sebastian Markbåge committed
May 10, 2016 at 18:24 UTC
cf157886e9f8f05c645c6c13cf0be2c41a8fafee
56 files changed
+398
src/renderers/noop/ReactNoop.js
new
+84
@@ -0,0 +1,84 @@
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 ReactNoop
10
+ * @flow
11
+ */
12
+
13
+/**
14
+ * This is a renderer of React that doesn't have a render target output.
15
+ * It is useful to demonstrate the internals of the reconciler in isolation
16
+ * and for testing semantics of reconciliation separate from the host
17
+ * environment.
18
+ */
19
+
20
+'use strict';
21
+
22
+var ReactFiberReconciler = require('ReactFiberReconciler');
23
+
24
+var scheduledHighPriCallback = null;
25
+var scheduledLowPriCallback = null;
26
+
27
+var NoopRenderer = ReactFiberReconciler({
28
+
29
+ createHostInstance() {
30
+
31
+ },
32
+ scheduleHighPriCallback(callback) {
33
+ scheduledHighPriCallback = callback;
34
+ },
35
+ scheduleLowPriCallback(callback) {
36
+ scheduledLowPriCallback = callback;
37
+ },
38
+
39
+});
40
+
41
+var ReactNoop = {
42
+
43
+ render(element : ReactElement) {
44
+
45
+ NoopRenderer.mountNewRoot(element);
46
+
47
+ },
48
+
49
+ flushHighPri() {
50
+ var cb = scheduledHighPriCallback;
51
+ if (cb === null) {
52
+ return;
53
+ }
54
+ scheduledHighPriCallback = null;
55
+ cb();
56
+ },
57
+
58
+ flushLowPri(timeout : number = Infinity) {
59
+ var cb = scheduledLowPriCallback;
60
+ if (cb === null) {
61
+ return;
62
+ }
63
+ scheduledLowPriCallback = null;
64
+ var timeRemaining = timeout;
65
+ cb({
66
+ timeRemaining() {
67
+ // Simulate a fix amount of time progressing between each call.
68
+ timeRemaining -= 5;
69
+ if (timeRemaining < 0) {
70
+ timeRemaining = 0;
71
+ }
72
+ return timeRemaining;
73
+ },
74
+ });
75
+ },
76
+
77
+ flush() {
78
+ ReactNoop.flushHighPri();
79
+ ReactNoop.flushLowPri();
80
+ },
81
+
82
+};
83
+
84
+module.exports = ReactNoop;
src/renderers/noop/__tests__/ReactNoop-test.js
new
+57
@@ -0,0 +1,57 @@
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
+ * @emails react-core
10
+ */
11
+
12
+'use strict';
13
+
14
+var React;
15
+var ReactNoop;
16
+
17
+describe('ReactComponent', function() {
18
+ beforeEach(function() {
19
+ React = require('React');
20
+ ReactNoop = require('ReactNoop');
21
+ });
22
+
23
+ it('should render a simple component', function() {
24
+
25
+ function Bar() {
26
+ return <div>Hello World</div>;
27
+ }
28
+
29
+ function Foo() {
30
+ return <Bar isBar={true} />;
31
+ }
32
+
33
+ ReactNoop.render(<Foo />);
34
+ ReactNoop.flush();
35
+
36
+ });
37
+
38
+ it('should render a simple component, in steps if needed', function() {
39
+
40
+ function Bar() {
41
+ return <div>Hello World</div>;
42
+ }
43
+
44
+ function Foo() {
45
+ return <Bar isBar={true} />;
46
+ }
47
+
48
+ ReactNoop.render(<Foo />);
49
+ // console.log('Nothing done');
50
+ ReactNoop.flushLowPri(7);
51
+ // console.log('Yield');
52
+ ReactNoop.flushLowPri(50);
53
+ // console.log('Done');
54
+ });
55
+
56
+
57
+});
src/renderers/shared/fiber/ReactFiber.js
new
+59
@@ -0,0 +1,59 @@
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 ReactFiber
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+type StateNode = {};
16
+type EffectHandler = () => void;
17
+type EffectTag = number;
18
+
19
+export type Fiber = {
20
+
21
+ tag: number,
22
+
23
+ parent: ?Fiber,
24
+ child: ?Fiber,
25
+ sibling: ?Fiber,
26
+
27
+ input: ?Object,
28
+ output: ?Object,
29
+
30
+ handler: EffectHandler,
31
+ handlerTag: EffectTag,
32
+
33
+ hasPendingChanges: bool,
34
+
35
+ stateNode: StateNode,
36
+
37
+};
38
+
39
+module.exports = function(tag : number) : Fiber {
40
+ return {
41
+
42
+ tag: tag,
43
+
44
+ parent: null,
45
+ child: null,
46
+ sibling: null,
47
+
48
+ input: null,
49
+ output: null,
50
+
51
+ handler: function() {},
52
+ handlerTag: 0,
53
+
54
+ hasPendingChanges: true,
55
+
56
+ stateNode: {},
57
+
58
+ };
59
+};
src/renderers/shared/fiber/ReactFiberFunctionalComponent.js
new
+45
@@ -0,0 +1,45 @@
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
new
+109
@@ -0,0 +1,109 @@
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 ReactFiberReconciler
10
+ * @flow
11
+ */
12
+
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
+ NativeComponent,
23
+} = ReactTypesOfWork;
24
+
25
+type ReactHostElement<T, P> = {
26
+ type: T,
27
+ props: P
28
+};
29
+
30
+type Deadline = {
31
+ timeRemaining : () => number
32
+};
33
+
34
+var timeHeuristicForUnitOfWork = 1;
35
+
36
+export type HostConfig<T, P, I> = {
37
+
38
+ createHostInstance(element : ReactHostElement<T, P>) : I,
39
+ scheduleHighPriCallback(callback : () => void) : void,
40
+ scheduleLowPriCallback(callback : (deadline : Deadline) => void) : void
41
+
42
+};
43
+
44
+type OpaqueID = {};
45
+
46
+export type Reconciler = {
47
+ mountNewRoot(element : ReactElement) : OpaqueID;
48
+};
49
+
50
+module.exports = function<T, P, I>(config : HostConfig<T, P, I>) : Reconciler {
51
+
52
+ // const scheduleHighPriCallback = config.scheduleHighPriCallback;
53
+ const scheduleLowPriCallback = config.scheduleLowPriCallback;
54
+
55
+ let nextUnitOfWork : ?Fiber = null;
56
+
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 NativeComponent:
64
+ break;
65
+ }
66
+ return null;
67
+ }
68
+
69
+ function performLowPriWork(deadline : Deadline) {
70
+ while (nextUnitOfWork) {
71
+ if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
72
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
73
+ } else {
74
+ scheduleLowPriCallback(performLowPriWork);
75
+ break;
76
+ }
77
+ }
78
+ }
79
+
80
+ function ensureLowPriIsScheduled() {
81
+ if (nextUnitOfWork) {
82
+ return;
83
+ }
84
+ scheduleLowPriCallback(performLowPriWork);
85
+ }
86
+
87
+ /*
88
+ function performHighPriWork() {
89
+ // There is no such thing as high pri work yet.
90
+ }
91
+
92
+ function ensureHighPriIsScheduled() {
93
+ scheduleHighPriCallback(performHighPriWork);
94
+ }
95
+ */
96
+
97
+ return {
98
+
99
+ mountNewRoot(element : ReactElement) : OpaqueID {
100
+
101
+ ensureLowPriIsScheduled();
102
+
103
+ nextUnitOfWork = ReactFiberFunctionalComponent.createFiber(element);
104
+
105
+ return {};
106
+ },
107
+
108
+ };
109
+};
src/renderers/shared/fiber/ReactStateNode.js
new
+23
@@ -0,0 +1,23 @@
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 ReactStateNode
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+type StateNode = {
16
+ next: ?{ [key: string]: StateNode },
17
+};
18
+
19
+module.exports = function() : StateNode {
20
+ return {
21
+ next: null,
22
+ };
23
+};
src/renderers/shared/fiber/ReactTypesOfWork.js
new
+21
@@ -0,0 +1,21 @@
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 ReactTypesOfWork
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+var TypesOfWork = {
16
+ FunctionalComponent: 1,
17
+ ClassComponent: 2,
18
+ NativeComponent: 3,
19
+};
20
+
21
+module.exports = TypesOfWork;