mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Dependency injection in the begin/complete/commit phases
This just makes them instantiable so that we can get access to the host config in these.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
import type { ReactCoroutine } from 'ReactCoroutine';
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { HostConfig } from 'ReactFiberReconciler';
|
||||
|
||||
var ReactChildFiber = require('ReactChildFiber');
|
||||
var ReactTypeOfWork = require('ReactTypeOfWork');
|
||||
@@ -33,211 +34,217 @@ var {
|
||||
} = require('ReactPriorityLevel');
|
||||
var { findNextUnitOfWorkAtPriority } = require('ReactFiberPendingWork');
|
||||
|
||||
function reconcileChildren(current, workInProgress, nextChildren) {
|
||||
const priority = workInProgress.pendingWorkPriority;
|
||||
workInProgress.child = ReactChildFiber.reconcileChildFibers(
|
||||
workInProgress,
|
||||
current ? current.child : null,
|
||||
nextChildren,
|
||||
priority
|
||||
);
|
||||
}
|
||||
module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
|
||||
|
||||
function updateFunctionalComponent(current, workInProgress) {
|
||||
var fn = workInProgress.type;
|
||||
var props = workInProgress.pendingProps;
|
||||
console.log('update fn:', fn.name);
|
||||
var nextChildren = fn(props);
|
||||
reconcileChildren(current, workInProgress, nextChildren);
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
}
|
||||
|
||||
function updateHostComponent(current, workInProgress) {
|
||||
console.log('host component', workInProgress.type, typeof workInProgress.pendingProps.children === 'string' ? workInProgress.pendingProps.children : '');
|
||||
|
||||
var nextChildren = workInProgress.pendingProps.children;
|
||||
|
||||
let priority = workInProgress.pendingWorkPriority;
|
||||
if (workInProgress.pendingProps.hidden && priority !== OffscreenPriority) {
|
||||
// If this host component is hidden, we can reconcile its children at
|
||||
// the lowest priority and bail out from this particular pass. Unless, we're
|
||||
// currently reconciling the lowest priority.
|
||||
workInProgress.child = ReactChildFiber.reconcileChildFibers(
|
||||
workInProgress,
|
||||
current ? current.child : null,
|
||||
nextChildren,
|
||||
OffscreenPriority
|
||||
);
|
||||
workInProgress.pendingWorkPriority = OffscreenPriority;
|
||||
return null;
|
||||
} else {
|
||||
function reconcileChildren(current, workInProgress, nextChildren) {
|
||||
const priority = workInProgress.pendingWorkPriority;
|
||||
workInProgress.child = ReactChildFiber.reconcileChildFibers(
|
||||
workInProgress,
|
||||
current ? current.child : null,
|
||||
nextChildren,
|
||||
priority
|
||||
);
|
||||
}
|
||||
|
||||
function updateFunctionalComponent(current, workInProgress) {
|
||||
var fn = workInProgress.type;
|
||||
var props = workInProgress.pendingProps;
|
||||
console.log('update fn:', fn.name);
|
||||
var nextChildren = fn(props);
|
||||
reconcileChildren(current, workInProgress, nextChildren);
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
return workInProgress.child;
|
||||
}
|
||||
}
|
||||
|
||||
function mountIndeterminateComponent(current, workInProgress) {
|
||||
var fn = workInProgress.type;
|
||||
var props = workInProgress.pendingProps;
|
||||
var value = fn(props);
|
||||
if (typeof value === 'object' && value && typeof value.render === 'function') {
|
||||
console.log('performed work on class:', fn.name);
|
||||
// Proceed under the assumption that this is a class instance
|
||||
workInProgress.tag = ClassComponent;
|
||||
if (workInProgress.alternate) {
|
||||
workInProgress.alternate.tag = ClassComponent;
|
||||
}
|
||||
} else {
|
||||
console.log('performed work on fn:', fn.name);
|
||||
// Proceed under the assumption that this is a functional component
|
||||
workInProgress.tag = FunctionalComponent;
|
||||
if (workInProgress.alternate) {
|
||||
workInProgress.alternate.tag = FunctionalComponent;
|
||||
function updateHostComponent(current, workInProgress) {
|
||||
console.log('host component', workInProgress.type, typeof workInProgress.pendingProps.children === 'string' ? workInProgress.pendingProps.children : '');
|
||||
|
||||
var nextChildren = workInProgress.pendingProps.children;
|
||||
|
||||
let priority = workInProgress.pendingWorkPriority;
|
||||
if (workInProgress.pendingProps.hidden && priority !== OffscreenPriority) {
|
||||
// If this host component is hidden, we can reconcile its children at
|
||||
// the lowest priority and bail out from this particular pass. Unless, we're
|
||||
// currently reconciling the lowest priority.
|
||||
workInProgress.child = ReactChildFiber.reconcileChildFibers(
|
||||
workInProgress,
|
||||
current ? current.child : null,
|
||||
nextChildren,
|
||||
OffscreenPriority
|
||||
);
|
||||
workInProgress.pendingWorkPriority = OffscreenPriority;
|
||||
return null;
|
||||
} else {
|
||||
workInProgress.child = ReactChildFiber.reconcileChildFibers(
|
||||
workInProgress,
|
||||
current ? current.child : null,
|
||||
nextChildren,
|
||||
priority
|
||||
);
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
return workInProgress.child;
|
||||
}
|
||||
}
|
||||
reconcileChildren(current, workInProgress, value);
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
}
|
||||
|
||||
function updateCoroutineComponent(current, workInProgress) {
|
||||
var coroutine = (workInProgress.pendingProps : ?ReactCoroutine);
|
||||
if (!coroutine) {
|
||||
throw new Error('Should be resolved by now');
|
||||
}
|
||||
console.log('begin coroutine', workInProgress.type.name);
|
||||
reconcileChildren(current, workInProgress, coroutine.children);
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
}
|
||||
|
||||
function reuseChildren(returnFiber : Fiber, firstChild : Fiber) {
|
||||
// TODO: None of this should be necessary if structured better.
|
||||
// The returnFiber pointer only needs to be updated when we walk into this child
|
||||
// which we don't do right now. If the pending work priority indicated only
|
||||
// if a child has work rather than if the node has work, then we would know
|
||||
// by a single lookup on workInProgress rather than having to go through
|
||||
// each child.
|
||||
let child = firstChild;
|
||||
do {
|
||||
// Update the returnFiber of the child to the newest fiber.
|
||||
child.return = returnFiber;
|
||||
// Retain the priority if there's any work left to do in the children.
|
||||
if (child.pendingWorkPriority !== NoWork &&
|
||||
(returnFiber.pendingWorkPriority === NoWork ||
|
||||
returnFiber.pendingWorkPriority > child.pendingWorkPriority)) {
|
||||
returnFiber.pendingWorkPriority = child.pendingWorkPriority;
|
||||
}
|
||||
} while (child = child.sibling);
|
||||
}
|
||||
|
||||
function beginWork(current : ?Fiber, workInProgress : Fiber) : ?Fiber {
|
||||
// The current, flushed, state of this fiber is the alternate.
|
||||
// Ideally nothing should rely on this, but relying on it here
|
||||
// means that we don't need an additional field on the work in
|
||||
// progress.
|
||||
if (current && workInProgress.pendingProps === current.memoizedProps) {
|
||||
// The most likely scenario is that the previous copy of the tree contains
|
||||
// the same props as the new one. In that case, we can just copy the output
|
||||
// and children from that node.
|
||||
workInProgress.memoizedProps = workInProgress.pendingProps;
|
||||
workInProgress.output = current.output;
|
||||
const priorityLevel = workInProgress.pendingWorkPriority;
|
||||
workInProgress.pendingProps = null;
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
workInProgress.stateNode = current.stateNode;
|
||||
if (current.child) {
|
||||
// If we bail out but still has work with the current priority in this
|
||||
// subtree, we need to go find it right now. If we don't, we won't flush
|
||||
// it until the next tick.
|
||||
workInProgress.child = current.child;
|
||||
reuseChildren(workInProgress, workInProgress.child);
|
||||
if (workInProgress.pendingWorkPriority <= priorityLevel) {
|
||||
// TODO: This passes the current node and reads the priority level and
|
||||
// pending props from that. We want it to read our priority level and
|
||||
// pending props from the work in progress. Needs restructuring.
|
||||
return findNextUnitOfWorkAtPriority(workInProgress.alternate, priorityLevel);
|
||||
} else {
|
||||
return null;
|
||||
function mountIndeterminateComponent(current, workInProgress) {
|
||||
var fn = workInProgress.type;
|
||||
var props = workInProgress.pendingProps;
|
||||
var value = fn(props);
|
||||
if (typeof value === 'object' && value && typeof value.render === 'function') {
|
||||
console.log('performed work on class:', fn.name);
|
||||
// Proceed under the assumption that this is a class instance
|
||||
workInProgress.tag = ClassComponent;
|
||||
if (workInProgress.alternate) {
|
||||
workInProgress.alternate.tag = ClassComponent;
|
||||
}
|
||||
} else {
|
||||
workInProgress.child = null;
|
||||
console.log('performed work on fn:', fn.name);
|
||||
// Proceed under the assumption that this is a functional component
|
||||
workInProgress.tag = FunctionalComponent;
|
||||
if (workInProgress.alternate) {
|
||||
workInProgress.alternate.tag = FunctionalComponent;
|
||||
}
|
||||
}
|
||||
reconcileChildren(current, workInProgress, value);
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
}
|
||||
|
||||
function updateCoroutineComponent(current, workInProgress) {
|
||||
var coroutine = (workInProgress.pendingProps : ?ReactCoroutine);
|
||||
if (!coroutine) {
|
||||
throw new Error('Should be resolved by now');
|
||||
}
|
||||
console.log('begin coroutine', workInProgress.type.name);
|
||||
reconcileChildren(current, workInProgress, coroutine.children);
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
}
|
||||
|
||||
function reuseChildren(returnFiber : Fiber, firstChild : Fiber) {
|
||||
// TODO: None of this should be necessary if structured better.
|
||||
// The returnFiber pointer only needs to be updated when we walk into this child
|
||||
// which we don't do right now. If the pending work priority indicated only
|
||||
// if a child has work rather than if the node has work, then we would know
|
||||
// by a single lookup on workInProgress rather than having to go through
|
||||
// each child.
|
||||
let child = firstChild;
|
||||
do {
|
||||
// Update the returnFiber of the child to the newest fiber.
|
||||
child.return = returnFiber;
|
||||
// Retain the priority if there's any work left to do in the children.
|
||||
if (child.pendingWorkPriority !== NoWork &&
|
||||
(returnFiber.pendingWorkPriority === NoWork ||
|
||||
returnFiber.pendingWorkPriority > child.pendingWorkPriority)) {
|
||||
returnFiber.pendingWorkPriority = child.pendingWorkPriority;
|
||||
}
|
||||
} while (child = child.sibling);
|
||||
}
|
||||
|
||||
function beginWork(current : ?Fiber, workInProgress : Fiber) : ?Fiber {
|
||||
// The current, flushed, state of this fiber is the alternate.
|
||||
// Ideally nothing should rely on this, but relying on it here
|
||||
// means that we don't need an additional field on the work in
|
||||
// progress.
|
||||
if (current && workInProgress.pendingProps === current.memoizedProps) {
|
||||
// The most likely scenario is that the previous copy of the tree contains
|
||||
// the same props as the new one. In that case, we can just copy the output
|
||||
// and children from that node.
|
||||
workInProgress.memoizedProps = workInProgress.pendingProps;
|
||||
workInProgress.output = current.output;
|
||||
const priorityLevel = workInProgress.pendingWorkPriority;
|
||||
workInProgress.pendingProps = null;
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
workInProgress.stateNode = current.stateNode;
|
||||
if (current.child) {
|
||||
// If we bail out but still has work with the current priority in this
|
||||
// subtree, we need to go find it right now. If we don't, we won't flush
|
||||
// it until the next tick.
|
||||
workInProgress.child = current.child;
|
||||
reuseChildren(workInProgress, workInProgress.child);
|
||||
if (workInProgress.pendingWorkPriority <= priorityLevel) {
|
||||
// TODO: This passes the current node and reads the priority level and
|
||||
// pending props from that. We want it to read our priority level and
|
||||
// pending props from the work in progress. Needs restructuring.
|
||||
return findNextUnitOfWorkAtPriority(workInProgress.alternate, priorityLevel);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
workInProgress.child = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!workInProgress.hasWorkInProgress &&
|
||||
workInProgress.pendingProps === workInProgress.memoizedProps &&
|
||||
workInProgress.pendingWorkPriority === NoWork) {
|
||||
// If we started this work before, and finished it, or if we're in a
|
||||
// ping-pong update scenario, this version could already be what we're
|
||||
// looking for. In that case, we should be able to just bail out.
|
||||
workInProgress.pendingProps = null;
|
||||
// TODO: We should be able to bail out if there is remaining work at a lower
|
||||
// priority too. However, I don't know if that is safe or even better since
|
||||
// the other tree could've potentially finished that work.
|
||||
return null;
|
||||
}
|
||||
|
||||
workInProgress.hasWorkInProgress = true;
|
||||
|
||||
switch (workInProgress.tag) {
|
||||
case IndeterminateComponent:
|
||||
mountIndeterminateComponent(current, workInProgress);
|
||||
return workInProgress.child;
|
||||
case FunctionalComponent:
|
||||
updateFunctionalComponent(current, workInProgress);
|
||||
return workInProgress.child;
|
||||
case ClassComponent:
|
||||
console.log('class component', workInProgress.pendingProps.type.name);
|
||||
return workInProgress.child;
|
||||
case HostContainer:
|
||||
reconcileChildren(current, workInProgress, workInProgress.pendingProps);
|
||||
// A yield component is just a placeholder, we can just run through the
|
||||
// next one immediately.
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
if (workInProgress.child) {
|
||||
return beginWork(
|
||||
workInProgress.child.alternate,
|
||||
workInProgress.child
|
||||
);
|
||||
}
|
||||
return null;
|
||||
case HostComponent:
|
||||
return updateHostComponent(current, workInProgress);
|
||||
case CoroutineHandlerPhase:
|
||||
// This is a restart. Reset the tag to the initial phase.
|
||||
workInProgress.tag = CoroutineComponent;
|
||||
// Intentionally fall through since this is now the same.
|
||||
case CoroutineComponent:
|
||||
updateCoroutineComponent(current, workInProgress);
|
||||
// This doesn't take arbitrary time so we could synchronously just begin
|
||||
// eagerly do the work of workInProgress.child as an optimization.
|
||||
if (workInProgress.child) {
|
||||
return beginWork(
|
||||
workInProgress.child.alternate,
|
||||
workInProgress.child
|
||||
);
|
||||
}
|
||||
return workInProgress.child;
|
||||
case YieldComponent:
|
||||
// A yield component is just a placeholder, we can just run through the
|
||||
// next one immediately.
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
if (workInProgress.sibling) {
|
||||
return beginWork(
|
||||
workInProgress.sibling.alternate,
|
||||
workInProgress.sibling
|
||||
);
|
||||
}
|
||||
return null;
|
||||
default:
|
||||
throw new Error('Unknown unit of work tag');
|
||||
}
|
||||
}
|
||||
|
||||
if (!workInProgress.hasWorkInProgress &&
|
||||
workInProgress.pendingProps === workInProgress.memoizedProps &&
|
||||
workInProgress.pendingWorkPriority === NoWork) {
|
||||
// If we started this work before, and finished it, or if we're in a
|
||||
// ping-pong update scenario, this version could already be what we're
|
||||
// looking for. In that case, we should be able to just bail out.
|
||||
workInProgress.pendingProps = null;
|
||||
// TODO: We should be able to bail out if there is remaining work at a lower
|
||||
// priority too. However, I don't know if that is safe or even better since
|
||||
// the other tree could've potentially finished that work.
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
beginWork,
|
||||
};
|
||||
|
||||
workInProgress.hasWorkInProgress = true;
|
||||
|
||||
switch (workInProgress.tag) {
|
||||
case IndeterminateComponent:
|
||||
mountIndeterminateComponent(current, workInProgress);
|
||||
return workInProgress.child;
|
||||
case FunctionalComponent:
|
||||
updateFunctionalComponent(current, workInProgress);
|
||||
return workInProgress.child;
|
||||
case ClassComponent:
|
||||
console.log('class component', workInProgress.pendingProps.type.name);
|
||||
return workInProgress.child;
|
||||
case HostContainer:
|
||||
reconcileChildren(current, workInProgress, workInProgress.pendingProps);
|
||||
// A yield component is just a placeholder, we can just run through the
|
||||
// next one immediately.
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
if (workInProgress.child) {
|
||||
return beginWork(
|
||||
workInProgress.child.alternate,
|
||||
workInProgress.child
|
||||
);
|
||||
}
|
||||
return null;
|
||||
case HostComponent:
|
||||
return updateHostComponent(current, workInProgress);
|
||||
case CoroutineHandlerPhase:
|
||||
// This is a restart. Reset the tag to the initial phase.
|
||||
workInProgress.tag = CoroutineComponent;
|
||||
// Intentionally fall through since this is now the same.
|
||||
case CoroutineComponent:
|
||||
updateCoroutineComponent(current, workInProgress);
|
||||
// This doesn't take arbitrary time so we could synchronously just begin
|
||||
// eagerly do the work of workInProgress.child as an optimization.
|
||||
if (workInProgress.child) {
|
||||
return beginWork(
|
||||
workInProgress.child.alternate,
|
||||
workInProgress.child
|
||||
);
|
||||
}
|
||||
return workInProgress.child;
|
||||
case YieldComponent:
|
||||
// A yield component is just a placeholder, we can just run through the
|
||||
// next one immediately.
|
||||
workInProgress.pendingWorkPriority = NoWork;
|
||||
if (workInProgress.sibling) {
|
||||
return beginWork(
|
||||
workInProgress.sibling.alternate,
|
||||
workInProgress.sibling
|
||||
);
|
||||
}
|
||||
return null;
|
||||
default:
|
||||
throw new Error('Unknown unit of work tag');
|
||||
}
|
||||
}
|
||||
|
||||
exports.beginWork = beginWork;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
'use strict';
|
||||
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { HostConfig } from 'ReactFiberReconciler';
|
||||
|
||||
var ReactTypeOfWork = require('ReactTypeOfWork');
|
||||
var {
|
||||
@@ -21,18 +22,26 @@ var {
|
||||
HostComponent,
|
||||
} = ReactTypeOfWork;
|
||||
|
||||
exports.commitWork = function(finishedWork : Fiber) : void {
|
||||
switch (finishedWork.tag) {
|
||||
case ClassComponent:
|
||||
// TODO: Fire componentDidMount/componentDidUpdate, update refs
|
||||
return;
|
||||
case HostContainer:
|
||||
// TODO: Attach children to root container.
|
||||
return;
|
||||
case HostComponent:
|
||||
console.log('commit updates to host component', finishedWork.type);
|
||||
return;
|
||||
default:
|
||||
throw new Error('This unit of work tag should not have side-effects.');
|
||||
module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
|
||||
|
||||
function commitWork(finishedWork : Fiber) : void {
|
||||
switch (finishedWork.tag) {
|
||||
case ClassComponent:
|
||||
// TODO: Fire componentDidMount/componentDidUpdate, update refs
|
||||
return;
|
||||
case HostContainer:
|
||||
// TODO: Attach children to root container.
|
||||
return;
|
||||
case HostComponent:
|
||||
console.log('commit updates to host component', finishedWork.type);
|
||||
return;
|
||||
default:
|
||||
throw new Error('This unit of work tag should not have side-effects.');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
commitWork,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
import type { ReactCoroutine } from 'ReactCoroutine';
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { HostConfig } from 'ReactFiberReconciler';
|
||||
|
||||
import type { ReifiedYield } from 'ReactReifiedYield';
|
||||
|
||||
@@ -30,117 +31,125 @@ var {
|
||||
YieldComponent,
|
||||
} = ReactTypeOfWork;
|
||||
|
||||
function markForPostEffect(workInProgress : Fiber) {
|
||||
// Schedule a side-effect on this fiber, after the children's side-effects.
|
||||
if (workInProgress.lastEffect) {
|
||||
workInProgress.lastEffect.nextEffect = workInProgress;
|
||||
} else {
|
||||
workInProgress.firstEffect = workInProgress;
|
||||
}
|
||||
workInProgress.lastEffect = workInProgress;
|
||||
}
|
||||
module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
|
||||
|
||||
function transferOutput(child : ?Fiber, returnFiber : Fiber) {
|
||||
// If we have a single result, we just pass that through as the output to
|
||||
// avoid unnecessary traversal. When we have multiple output, we just pass
|
||||
// the linked list of fibers that has the individual output values.
|
||||
returnFiber.output = (child && !child.sibling) ? child.output : child;
|
||||
returnFiber.memoizedProps = returnFiber.pendingProps;
|
||||
}
|
||||
|
||||
function recursivelyFillYields(yields, output : ?Fiber | ?ReifiedYield) {
|
||||
if (!output) {
|
||||
// Ignore nulls etc.
|
||||
} else if (output.tag !== undefined) { // TODO: Fix this fragile duck test.
|
||||
// Detect if this is a fiber, if so it is a fragment result.
|
||||
// $FlowFixMe: Refinement issue.
|
||||
var item = (output : Fiber);
|
||||
do {
|
||||
recursivelyFillYields(yields, item.output);
|
||||
item = item.sibling;
|
||||
} while (item);
|
||||
} else {
|
||||
// $FlowFixMe: Refinement issue. If it is not a Fiber or null, it is a yield
|
||||
yields.push(output);
|
||||
}
|
||||
}
|
||||
|
||||
function moveCoroutineToHandlerPhase(current : ?Fiber, workInProgress : Fiber) {
|
||||
var coroutine = (workInProgress.pendingProps : ?ReactCoroutine);
|
||||
if (!coroutine) {
|
||||
throw new Error('Should be resolved by now');
|
||||
function markForPostEffect(workInProgress : Fiber) {
|
||||
// Schedule a side-effect on this fiber, after the children's side-effects.
|
||||
if (workInProgress.lastEffect) {
|
||||
workInProgress.lastEffect.nextEffect = workInProgress;
|
||||
} else {
|
||||
workInProgress.firstEffect = workInProgress;
|
||||
}
|
||||
workInProgress.lastEffect = workInProgress;
|
||||
}
|
||||
|
||||
// First step of the coroutine has completed. Now we need to do the second.
|
||||
// TODO: It would be nice to have a multi stage coroutine represented by a
|
||||
// single component, or at least tail call optimize nested ones. Currently
|
||||
// that requires additional fields that we don't want to add to the fiber.
|
||||
// So this requires nested handlers.
|
||||
// Note: This doesn't mutate the alternate node. I don't think it needs to
|
||||
// since this stage is reset for every pass.
|
||||
workInProgress.tag = CoroutineHandlerPhase;
|
||||
|
||||
// Build up the yields.
|
||||
// TODO: Compare this to a generator or opaque helpers like Children.
|
||||
var yields : Array<ReifiedYield> = [];
|
||||
var child = workInProgress.child;
|
||||
while (child) {
|
||||
recursivelyFillYields(yields, child.output);
|
||||
child = child.sibling;
|
||||
function transferOutput(child : ?Fiber, returnFiber : Fiber) {
|
||||
// If we have a single result, we just pass that through as the output to
|
||||
// avoid unnecessary traversal. When we have multiple output, we just pass
|
||||
// the linked list of fibers that has the individual output values.
|
||||
returnFiber.output = (child && !child.sibling) ? child.output : child;
|
||||
returnFiber.memoizedProps = returnFiber.pendingProps;
|
||||
}
|
||||
var fn = coroutine.handler;
|
||||
var props = coroutine.props;
|
||||
var nextChildren = fn(props, yields);
|
||||
|
||||
var currentFirstChild = current ? current.stateNode : null;
|
||||
// Inherit the priority of the returnFiber.
|
||||
const priority = workInProgress.pendingWorkPriority;
|
||||
workInProgress.stateNode = ReactChildFiber.reconcileChildFibers(
|
||||
workInProgress,
|
||||
currentFirstChild,
|
||||
nextChildren,
|
||||
priority
|
||||
);
|
||||
return workInProgress.stateNode;
|
||||
}
|
||||
|
||||
exports.completeWork = function(current : ?Fiber, workInProgress : Fiber) : ?Fiber {
|
||||
switch (workInProgress.tag) {
|
||||
case FunctionalComponent:
|
||||
console.log('/functional component', workInProgress.type.name);
|
||||
transferOutput(workInProgress.child, workInProgress);
|
||||
return null;
|
||||
case ClassComponent:
|
||||
console.log('/class component', workInProgress.type.name);
|
||||
transferOutput(workInProgress.child, workInProgress);
|
||||
return null;
|
||||
case HostContainer:
|
||||
return null;
|
||||
case HostComponent:
|
||||
transferOutput(workInProgress.child, workInProgress);
|
||||
if (workInProgress.alternate) {
|
||||
// If we have an alternate, that means this is an update and we need to
|
||||
// schedule a side-effect to do the updates.
|
||||
markForPostEffect(workInProgress);
|
||||
}
|
||||
console.log('/host component', workInProgress.type);
|
||||
return null;
|
||||
case CoroutineComponent:
|
||||
console.log('/coroutine component', workInProgress.pendingProps.handler.name);
|
||||
return moveCoroutineToHandlerPhase(current, workInProgress);
|
||||
case CoroutineHandlerPhase:
|
||||
transferOutput(workInProgress.stateNode, workInProgress);
|
||||
// Reset the tag to now be a first phase coroutine.
|
||||
workInProgress.tag = CoroutineComponent;
|
||||
return null;
|
||||
case YieldComponent:
|
||||
// Does nothing.
|
||||
return null;
|
||||
|
||||
// Error cases
|
||||
case IndeterminateComponent:
|
||||
throw new Error('An indeterminate component should have become determinate before completing.');
|
||||
default:
|
||||
throw new Error('Unknown unit of work tag');
|
||||
function recursivelyFillYields(yields, output : ?Fiber | ?ReifiedYield) {
|
||||
if (!output) {
|
||||
// Ignore nulls etc.
|
||||
} else if (output.tag !== undefined) { // TODO: Fix this fragile duck test.
|
||||
// Detect if this is a fiber, if so it is a fragment result.
|
||||
// $FlowFixMe: Refinement issue.
|
||||
var item = (output : Fiber);
|
||||
do {
|
||||
recursivelyFillYields(yields, item.output);
|
||||
item = item.sibling;
|
||||
} while (item);
|
||||
} else {
|
||||
// $FlowFixMe: Refinement issue. If it is not a Fiber or null, it is a yield
|
||||
yields.push(output);
|
||||
}
|
||||
}
|
||||
|
||||
function moveCoroutineToHandlerPhase(current : ?Fiber, workInProgress : Fiber) {
|
||||
var coroutine = (workInProgress.pendingProps : ?ReactCoroutine);
|
||||
if (!coroutine) {
|
||||
throw new Error('Should be resolved by now');
|
||||
}
|
||||
|
||||
// First step of the coroutine has completed. Now we need to do the second.
|
||||
// TODO: It would be nice to have a multi stage coroutine represented by a
|
||||
// single component, or at least tail call optimize nested ones. Currently
|
||||
// that requires additional fields that we don't want to add to the fiber.
|
||||
// So this requires nested handlers.
|
||||
// Note: This doesn't mutate the alternate node. I don't think it needs to
|
||||
// since this stage is reset for every pass.
|
||||
workInProgress.tag = CoroutineHandlerPhase;
|
||||
|
||||
// Build up the yields.
|
||||
// TODO: Compare this to a generator or opaque helpers like Children.
|
||||
var yields : Array<ReifiedYield> = [];
|
||||
var child = workInProgress.child;
|
||||
while (child) {
|
||||
recursivelyFillYields(yields, child.output);
|
||||
child = child.sibling;
|
||||
}
|
||||
var fn = coroutine.handler;
|
||||
var props = coroutine.props;
|
||||
var nextChildren = fn(props, yields);
|
||||
|
||||
var currentFirstChild = current ? current.stateNode : null;
|
||||
// Inherit the priority of the returnFiber.
|
||||
const priority = workInProgress.pendingWorkPriority;
|
||||
workInProgress.stateNode = ReactChildFiber.reconcileChildFibers(
|
||||
workInProgress,
|
||||
currentFirstChild,
|
||||
nextChildren,
|
||||
priority
|
||||
);
|
||||
return workInProgress.stateNode;
|
||||
}
|
||||
|
||||
function completeWork(current : ?Fiber, workInProgress : Fiber) : ?Fiber {
|
||||
switch (workInProgress.tag) {
|
||||
case FunctionalComponent:
|
||||
console.log('/functional component', workInProgress.type.name);
|
||||
transferOutput(workInProgress.child, workInProgress);
|
||||
return null;
|
||||
case ClassComponent:
|
||||
console.log('/class component', workInProgress.type.name);
|
||||
transferOutput(workInProgress.child, workInProgress);
|
||||
return null;
|
||||
case HostContainer:
|
||||
return null;
|
||||
case HostComponent:
|
||||
transferOutput(workInProgress.child, workInProgress);
|
||||
if (workInProgress.alternate) {
|
||||
// If we have an alternate, that means this is an update and we need to
|
||||
// schedule a side-effect to do the updates.
|
||||
markForPostEffect(workInProgress);
|
||||
}
|
||||
console.log('/host component', workInProgress.type);
|
||||
return null;
|
||||
case CoroutineComponent:
|
||||
console.log('/coroutine component', workInProgress.pendingProps.handler.name);
|
||||
return moveCoroutineToHandlerPhase(current, workInProgress);
|
||||
case CoroutineHandlerPhase:
|
||||
transferOutput(workInProgress.stateNode, workInProgress);
|
||||
// Reset the tag to now be a first phase coroutine.
|
||||
workInProgress.tag = CoroutineComponent;
|
||||
return null;
|
||||
case YieldComponent:
|
||||
// Does nothing.
|
||||
return null;
|
||||
|
||||
// Error cases
|
||||
case IndeterminateComponent:
|
||||
throw new Error('An indeterminate component should have become determinate before completing.');
|
||||
default:
|
||||
throw new Error('Unknown unit of work tag');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
completeWork,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -16,10 +16,11 @@ import type { Fiber } from 'ReactFiber';
|
||||
import type { FiberRoot } from 'ReactFiberRoot';
|
||||
import type { HostConfig } from 'ReactFiberReconciler';
|
||||
|
||||
var ReactFiberBeginWork = require('ReactFiberBeginWork');
|
||||
var ReactFiberCompleteWork = require('ReactFiberCompleteWork');
|
||||
var ReactFiberCommitWork = require('ReactFiberCommitWork');
|
||||
|
||||
var { cloneFiber } = require('ReactFiber');
|
||||
var { beginWork } = require('ReactFiberBeginWork');
|
||||
var { completeWork } = require('ReactFiberCompleteWork');
|
||||
var { commitWork } = require('ReactFiberCommitWork');
|
||||
var { findNextUnitOfWorkAtPriority } = require('ReactFiberPendingWork');
|
||||
|
||||
var {
|
||||
@@ -33,6 +34,10 @@ var timeHeuristicForUnitOfWork = 1;
|
||||
|
||||
module.exports = function<T, P, I>(config : HostConfig<T, P, I>) {
|
||||
|
||||
const { beginWork } = ReactFiberBeginWork(config);
|
||||
const { completeWork } = ReactFiberCompleteWork(config);
|
||||
const { commitWork } = ReactFiberCommitWork(config);
|
||||
|
||||
// const scheduleHighPriCallback = config.scheduleHighPriCallback;
|
||||
const scheduleLowPriCallback = config.scheduleLowPriCallback;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user