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 cf157886e9)
This commit is contained in:
Sebastian Markbåge
2016-06-14 15:30:26 -07:00
committed by Paul O’Shannessy
parent 484a5b38af
commit 69208fc62a
56 changed files with 398 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNoop
* @flow
*/
/**
* This is a renderer of React that doesn't have a render target output.
* It is useful to demonstrate the internals of the reconciler in isolation
* and for testing semantics of reconciliation separate from the host
* environment.
*/
'use strict';
var ReactFiberReconciler = require('ReactFiberReconciler');
var scheduledHighPriCallback = null;
var scheduledLowPriCallback = null;
var NoopRenderer = ReactFiberReconciler({
createHostInstance() {
},
scheduleHighPriCallback(callback) {
scheduledHighPriCallback = callback;
},
scheduleLowPriCallback(callback) {
scheduledLowPriCallback = callback;
},
});
var ReactNoop = {
render(element : ReactElement) {
NoopRenderer.mountNewRoot(element);
},
flushHighPri() {
var cb = scheduledHighPriCallback;
if (cb === null) {
return;
}
scheduledHighPriCallback = null;
cb();
},
flushLowPri(timeout : number = Infinity) {
var cb = scheduledLowPriCallback;
if (cb === null) {
return;
}
scheduledLowPriCallback = null;
var timeRemaining = timeout;
cb({
timeRemaining() {
// Simulate a fix amount of time progressing between each call.
timeRemaining -= 5;
if (timeRemaining < 0) {
timeRemaining = 0;
}
return timeRemaining;
},
});
},
flush() {
ReactNoop.flushHighPri();
ReactNoop.flushLowPri();
},
};
module.exports = ReactNoop;
@@ -0,0 +1,57 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React;
var ReactNoop;
describe('ReactComponent', function() {
beforeEach(function() {
React = require('React');
ReactNoop = require('ReactNoop');
});
it('should render a simple component', function() {
function Bar() {
return <div>Hello World</div>;
}
function Foo() {
return <Bar isBar={true} />;
}
ReactNoop.render(<Foo />);
ReactNoop.flush();
});
it('should render a simple component, in steps if needed', function() {
function Bar() {
return <div>Hello World</div>;
}
function Foo() {
return <Bar isBar={true} />;
}
ReactNoop.render(<Foo />);
// console.log('Nothing done');
ReactNoop.flushLowPri(7);
// console.log('Yield');
ReactNoop.flushLowPri(50);
// console.log('Done');
});
});
+59
View File
@@ -0,0 +1,59 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiber
* @flow
*/
'use strict';
type StateNode = {};
type EffectHandler = () => void;
type EffectTag = number;
export type Fiber = {
tag: number,
parent: ?Fiber,
child: ?Fiber,
sibling: ?Fiber,
input: ?Object,
output: ?Object,
handler: EffectHandler,
handlerTag: EffectTag,
hasPendingChanges: bool,
stateNode: StateNode,
};
module.exports = function(tag : number) : Fiber {
return {
tag: tag,
parent: null,
child: null,
sibling: null,
input: null,
output: null,
handler: function() {},
handlerTag: 0,
hasPendingChanges: true,
stateNode: {},
};
};
@@ -0,0 +1,45 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberFunctionalComponent
* @flow
*/
'use strict';
import type { Fiber } from 'ReactFiber';
var createFiber = require('ReactFiber');
var ReactTypesOfWork = require('ReactTypesOfWork');
var {
FunctionalComponent,
} = ReactTypesOfWork;
exports.performWork = function(unitOfWork : Fiber) : ?Fiber {
var element = unitOfWork.input;
if (!element) {
throw new Error('Should be resolved by now');
}
var fn = element.type;
var props = element.props;
// console.log('perform work on:', fn.name);
var nextElement = fn(props);
if (typeof nextElement.type === 'function') {
return exports.createFiber(nextElement);
}
return null;
};
exports.createFiber = function(element : ReactElement) {
var fiber = createFiber(
FunctionalComponent
);
fiber.input = element;
return fiber;
};
@@ -0,0 +1,109 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactFiberReconciler
* @flow
*/
'use strict';
import type { Fiber } from 'ReactFiber';
var ReactFiberFunctionalComponent = require('ReactFiberFunctionalComponent');
var ReactTypesOfWork = require('ReactTypesOfWork');
var {
FunctionalComponent,
ClassComponent,
NativeComponent,
} = ReactTypesOfWork;
type ReactHostElement<T, P> = {
type: T,
props: P
};
type Deadline = {
timeRemaining : () => number
};
var timeHeuristicForUnitOfWork = 1;
export type HostConfig<T, P, I> = {
createHostInstance(element : ReactHostElement<T, P>) : I,
scheduleHighPriCallback(callback : () => void) : void,
scheduleLowPriCallback(callback : (deadline : Deadline) => void) : void
};
type OpaqueID = {};
export type Reconciler = {
mountNewRoot(element : ReactElement) : OpaqueID;
};
module.exports = function<T, P, I>(config : HostConfig<T, P, I>) : Reconciler {
// const scheduleHighPriCallback = config.scheduleHighPriCallback;
const scheduleLowPriCallback = config.scheduleLowPriCallback;
let nextUnitOfWork : ?Fiber = null;
function performUnitOfWork(unit : Fiber) : ?Fiber {
switch (unit.tag) {
case FunctionalComponent:
return ReactFiberFunctionalComponent.performWork(unit);
case ClassComponent:
break;
case NativeComponent:
break;
}
return null;
}
function performLowPriWork(deadline : Deadline) {
while (nextUnitOfWork) {
if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
} else {
scheduleLowPriCallback(performLowPriWork);
break;
}
}
}
function ensureLowPriIsScheduled() {
if (nextUnitOfWork) {
return;
}
scheduleLowPriCallback(performLowPriWork);
}
/*
function performHighPriWork() {
// There is no such thing as high pri work yet.
}
function ensureHighPriIsScheduled() {
scheduleHighPriCallback(performHighPriWork);
}
*/
return {
mountNewRoot(element : ReactElement) : OpaqueID {
ensureLowPriIsScheduled();
nextUnitOfWork = ReactFiberFunctionalComponent.createFiber(element);
return {};
},
};
};
@@ -0,0 +1,23 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactStateNode
* @flow
*/
'use strict';
type StateNode = {
next: ?{ [key: string]: StateNode },
};
module.exports = function() : StateNode {
return {
next: null,
};
};
@@ -0,0 +1,21 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactTypesOfWork
* @flow
*/
'use strict';
var TypesOfWork = {
FunctionalComponent: 1,
ClassComponent: 2,
NativeComponent: 3,
};
module.exports = TypesOfWork;