188 lines
6.1 KiB
JavaScript
188 lines
6.1 KiB
JavaScript
|
/**
|
||
|
* 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.
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var ReactTypeOfWork = require('./ReactTypeOfWork');
|
||
|
var IndeterminateComponent = ReactTypeOfWork.IndeterminateComponent,
|
||
|
ClassComponent = ReactTypeOfWork.ClassComponent,
|
||
|
HostContainer = ReactTypeOfWork.HostContainer,
|
||
|
HostComponent = ReactTypeOfWork.HostComponent,
|
||
|
CoroutineComponent = ReactTypeOfWork.CoroutineComponent,
|
||
|
YieldComponent = ReactTypeOfWork.YieldComponent;
|
||
|
|
||
|
var _require = require('./ReactPriorityLevel'),
|
||
|
NoWork = _require.NoWork;
|
||
|
|
||
|
// An Instance is shared between all versions of a component. We can easily
|
||
|
// break this out into a separate object to avoid copying so much to the
|
||
|
// alternate versions of the tree. We put this on a single object for now to
|
||
|
// minimize the number of objects created during the initial render.
|
||
|
|
||
|
|
||
|
// A Fiber is work on a Component that needs to be done or was done. There can
|
||
|
// be more than one per component.
|
||
|
|
||
|
|
||
|
// This is a constructor of a POJO instead of a constructor function for a few
|
||
|
// reasons:
|
||
|
// 1) Nobody should add any instance methods on this. Instance methods can be
|
||
|
// more difficult to predict when they get optimized and they are almost
|
||
|
// never inlined properly in static compilers.
|
||
|
// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
|
||
|
// always know when it is a fiber.
|
||
|
// 3) We can easily go from a createFiber call to calling a constructor if that
|
||
|
// is faster. The opposite is not true.
|
||
|
// 4) We might want to experiment with using numeric keys since they are easier
|
||
|
// to optimize in a non-JIT environment.
|
||
|
// 5) It should be easy to port this to a C struct and keep a C implementation
|
||
|
// compatible.
|
||
|
var createFiber = function (tag, key) {
|
||
|
return {
|
||
|
// Instance
|
||
|
|
||
|
tag: tag,
|
||
|
|
||
|
key: key,
|
||
|
|
||
|
type: null,
|
||
|
|
||
|
stateNode: null,
|
||
|
|
||
|
// Fiber
|
||
|
|
||
|
'return': null,
|
||
|
|
||
|
child: null,
|
||
|
sibling: null,
|
||
|
|
||
|
ref: null,
|
||
|
|
||
|
pendingProps: null,
|
||
|
memoizedProps: null,
|
||
|
updateQueue: null,
|
||
|
memoizedState: null,
|
||
|
callbackList: null,
|
||
|
output: null,
|
||
|
|
||
|
nextEffect: null,
|
||
|
firstEffect: null,
|
||
|
lastEffect: null,
|
||
|
|
||
|
pendingWorkPriority: NoWork,
|
||
|
progressedPriority: NoWork,
|
||
|
progressedChild: null,
|
||
|
|
||
|
alternate: null
|
||
|
};
|
||
|
};
|
||
|
|
||
|
function shouldConstruct(Component) {
|
||
|
return !!(Component.prototype && Component.prototype.isReactComponent);
|
||
|
}
|
||
|
|
||
|
// This is used to create an alternate fiber to do work on.
|
||
|
// TODO: Rename to createWorkInProgressFiber or something like that.
|
||
|
exports.cloneFiber = function (fiber, priorityLevel) {
|
||
|
// We clone to get a work in progress. That means that this fiber is the
|
||
|
// current. To make it safe to reuse that fiber later on as work in progress
|
||
|
// we need to reset its work in progress flag now. We don't have an
|
||
|
// opportunity to do this earlier since we don't traverse the tree when
|
||
|
// the work in progress tree becomes the current tree.
|
||
|
// fiber.progressedPriority = NoWork;
|
||
|
// fiber.progressedChild = null;
|
||
|
|
||
|
// We use a double buffering pooling technique because we know that we'll only
|
||
|
// ever need at most two versions of a tree. We pool the "other" unused node
|
||
|
// that we're free to reuse. This is lazily created to avoid allocating extra
|
||
|
// objects for things that are never updated. It also allow us to reclaim the
|
||
|
// extra memory if needed.
|
||
|
var alt = fiber.alternate;
|
||
|
if (alt) {
|
||
|
// Whenever we clone, we do so to get a new work in progress.
|
||
|
// This ensures that we've reset these in the new tree.
|
||
|
alt.nextEffect = null;
|
||
|
alt.firstEffect = null;
|
||
|
alt.lastEffect = null;
|
||
|
} else {
|
||
|
// This should not have an alternate already
|
||
|
alt = createFiber(fiber.tag, fiber.key);
|
||
|
alt.type = fiber.type;
|
||
|
|
||
|
alt.progressedChild = fiber.progressedChild;
|
||
|
alt.progressedPriority = fiber.progressedPriority;
|
||
|
|
||
|
alt.alternate = fiber;
|
||
|
fiber.alternate = alt;
|
||
|
}
|
||
|
|
||
|
alt.stateNode = fiber.stateNode;
|
||
|
alt.child = fiber.child;
|
||
|
alt.sibling = fiber.sibling; // This should always be overridden. TODO: null
|
||
|
alt.ref = fiber.ref;
|
||
|
// pendingProps is here for symmetry but is unnecessary in practice for now.
|
||
|
// TODO: Pass in the new pendingProps as an argument maybe?
|
||
|
alt.pendingProps = fiber.pendingProps;
|
||
|
alt.updateQueue = fiber.updateQueue;
|
||
|
alt.callbackList = fiber.callbackList;
|
||
|
alt.pendingWorkPriority = priorityLevel;
|
||
|
|
||
|
alt.memoizedProps = fiber.memoizedProps;
|
||
|
alt.output = fiber.output;
|
||
|
|
||
|
return alt;
|
||
|
};
|
||
|
|
||
|
exports.createHostContainerFiber = function () {
|
||
|
var fiber = createFiber(HostContainer, null);
|
||
|
return fiber;
|
||
|
};
|
||
|
|
||
|
exports.createFiberFromElement = function (element, priorityLevel) {
|
||
|
// $FlowFixMe: ReactElement.key is currently defined as ?string but should be defined as null | string in Flow.
|
||
|
var fiber = createFiberFromElementType(element.type, element.key);
|
||
|
fiber.pendingProps = element.props;
|
||
|
fiber.pendingWorkPriority = priorityLevel;
|
||
|
return fiber;
|
||
|
};
|
||
|
|
||
|
function createFiberFromElementType(type, key) {
|
||
|
var fiber = void 0;
|
||
|
if (typeof type === 'function') {
|
||
|
fiber = shouldConstruct(type) ? createFiber(ClassComponent, key) : createFiber(IndeterminateComponent, key);
|
||
|
fiber.type = type;
|
||
|
} else if (typeof type === 'string') {
|
||
|
fiber = createFiber(HostComponent, key);
|
||
|
fiber.type = type;
|
||
|
} else if (typeof type === 'object' && type !== null) {
|
||
|
// Currently assumed to be a continuation and therefore is a fiber already.
|
||
|
fiber = type;
|
||
|
} else {
|
||
|
throw new Error('Unknown component type: ' + typeof type);
|
||
|
}
|
||
|
return fiber;
|
||
|
}
|
||
|
|
||
|
exports.createFiberFromElementType = createFiberFromElementType;
|
||
|
|
||
|
exports.createFiberFromCoroutine = function (coroutine, priorityLevel) {
|
||
|
var fiber = createFiber(CoroutineComponent, coroutine.key);
|
||
|
fiber.type = coroutine.handler;
|
||
|
fiber.pendingProps = coroutine;
|
||
|
fiber.pendingWorkPriority = priorityLevel;
|
||
|
return fiber;
|
||
|
};
|
||
|
|
||
|
exports.createFiberFromYield = function (yieldNode, priorityLevel) {
|
||
|
var fiber = createFiber(YieldComponent, yieldNode.key);
|
||
|
fiber.pendingProps = {};
|
||
|
return fiber;
|
||
|
};
|