Skip to content

Commit 69208fc

Browse files
sebmarkbagezpao
authored andcommitted
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. (cherry picked from commit cf15788)
1 parent 484a5b3 commit 69208fc

File tree

56 files changed

+398
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+398
-0
lines changed

src/renderers/noop/ReactNoop.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
};
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
};

0 commit comments

Comments
 (0)