Skip to content

Commit

Permalink
React Fiber Reconciler
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sebmarkbage committed May 10, 2016
1 parent 68bf0f0 commit 4904f8e
Show file tree
Hide file tree
Showing 56 changed files with 398 additions and 0 deletions.
84 changes: 84 additions & 0 deletions src/renderers/noop/ReactNoop.js
Original file line number Diff line number Diff line change
@@ -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;
57 changes: 57 additions & 0 deletions src/renderers/noop/__tests__/ReactNoop-test.js
Original file line number Diff line number Diff line change
@@ -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 changes: 59 additions & 0 deletions src/renderers/shared/fiber/ReactFiber.js
Original file line number Diff line number Diff line change
@@ -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: {}

};
}
47 changes: 47 additions & 0 deletions src/renderers/shared/fiber/ReactFiberFunctionalComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* 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,
ClassComponent,
NativeComponent,
} = 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(
ReactTypesOfWork.FunctionalComponent
);
fiber.input = element;
return fiber;
};
107 changes: 107 additions & 0 deletions src/renderers/shared/fiber/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* 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) : 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 {};
},

};
}
23 changes: 23 additions & 0 deletions src/renderers/shared/fiber/ReactStateNode.js
Original file line number Diff line number Diff line change
@@ -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
};
}
Loading

0 comments on commit 4904f8e

Please sign in to comment.