@samitouri / QOS-React / commits / cf157886e9

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;
src/renderers/shared/shared/shouldUpdateReactComponent.js renamed
src/renderers/shared/stack/event/EventConstants.js renamed
src/renderers/shared/stack/event/EventPluginHub.js renamed
src/renderers/shared/stack/event/EventPluginRegistry.js renamed
src/renderers/shared/stack/event/EventPluginUtils.js renamed
src/renderers/shared/stack/event/EventPropagators.js renamed
src/renderers/shared/stack/event/__tests__/EventPluginHub-test.js renamed
src/renderers/shared/stack/event/__tests__/EventPluginRegistry-test.js renamed
src/renderers/shared/stack/event/eventPlugins/ResponderEventPlugin.js renamed
src/renderers/shared/stack/event/eventPlugins/ResponderSyntheticEvent.js renamed
src/renderers/shared/stack/event/eventPlugins/ResponderTouchHistoryStore.js renamed
src/renderers/shared/stack/event/eventPlugins/TouchHistoryMath.js renamed
src/renderers/shared/stack/event/eventPlugins/__tests__/ResponderEventPlugin-test.js renamed
src/renderers/shared/stack/reconciler/ReactChildReconciler.js renamed
src/renderers/shared/stack/reconciler/ReactComponentEnvironment.js renamed
src/renderers/shared/stack/reconciler/ReactCompositeComponent.js renamed
src/renderers/shared/stack/reconciler/ReactDefaultBatchingStrategy.js renamed
src/renderers/shared/stack/reconciler/ReactEmptyComponent.js renamed
src/renderers/shared/stack/reconciler/ReactEventEmitterMixin.js renamed
src/renderers/shared/stack/reconciler/ReactInstanceHandles.js renamed
src/renderers/shared/stack/reconciler/ReactInstanceMap.js renamed
src/renderers/shared/stack/reconciler/ReactMultiChild.js renamed
src/renderers/shared/stack/reconciler/ReactMultiChildUpdateTypes.js renamed
src/renderers/shared/stack/reconciler/ReactNativeComponent.js renamed
src/renderers/shared/stack/reconciler/ReactOwner.js renamed
src/renderers/shared/stack/reconciler/ReactReconciler.js renamed
src/renderers/shared/stack/reconciler/ReactRef.js renamed
src/renderers/shared/stack/reconciler/ReactSimpleEmptyComponent.js renamed
src/renderers/shared/stack/reconciler/ReactStateSetters.js renamed
src/renderers/shared/stack/reconciler/ReactUpdateQueue.js renamed
src/renderers/shared/stack/reconciler/ReactUpdates.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactComponent-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactComponentLifeCycle-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponent-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentDOMMinimalism-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentNestedState-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentState-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactEmptyComponent-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactIdentity-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactMockedComponent-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactMultiChild-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactMultiChildReconcile-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactMultiChildText-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactStateSetters-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactStatelessComponent-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/ReactUpdates-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/refs-destruction-test.js renamed
src/renderers/shared/stack/reconciler/__tests__/refs-test.js renamed
src/renderers/shared/stack/reconciler/instantiateReactComponent.js renamed