mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
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:
committed by
Paul O’Shannessy
parent
484a5b38af
commit
69208fc62a
@@ -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');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user