diff --git a/src/isomorphic/classic/element/ReactElement.js b/src/isomorphic/classic/element/ReactElement.js
index 4e815449247af..98af361b097ba 100644
--- a/src/isomorphic/classic/element/ReactElement.js
+++ b/src/isomorphic/classic/element/ReactElement.js
@@ -398,4 +398,6 @@ ReactElement.isValidElement = function(object) {
);
};
+ReactElement.REACT_ELEMENT_TYPE = REACT_ELEMENT_TYPE;
+
module.exports = ReactElement;
diff --git a/src/isomorphic/classic/element/ReactElementValidator.js b/src/isomorphic/classic/element/ReactElementValidator.js
index 2b7e9da3d56ad..4d4121f9810c3 100644
--- a/src/isomorphic/classic/element/ReactElementValidator.js
+++ b/src/isomorphic/classic/element/ReactElementValidator.js
@@ -184,7 +184,8 @@ function validatePropTypes(element) {
var ReactElementValidator = {
createElement: function(type, props, children) {
- var validType = typeof type === 'string' || typeof type === 'function';
+ var validType = typeof type === 'string' || typeof type === 'function' ||
+ (type !== null && typeof type === 'object');
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
warning(
diff --git a/src/renderers/noop/__tests__/ReactNoop-test.js b/src/renderers/noop/__tests__/ReactNoop-test.js
index b71978e2cd75b..1ce47ee5c2dca 100644
--- a/src/renderers/noop/__tests__/ReactNoop-test.js
+++ b/src/renderers/noop/__tests__/ReactNoop-test.js
@@ -13,11 +13,14 @@
var React;
var ReactNoop;
+var ReactCoroutine;
describe('ReactComponent', function() {
beforeEach(function() {
React = require('React');
ReactNoop = require('ReactNoop');
+ ReactCoroutine = require('ReactCoroutine');
+ spyOn(console, 'log');
});
it('should render a simple component', function() {
@@ -38,11 +41,14 @@ describe('ReactComponent', function() {
it('should render a simple component, in steps if needed', function() {
function Bar() {
- return
Hello World
;
+ return Hello World
;
}
function Foo() {
- return ;
+ return [
+ ,
+ ,
+ ];
}
ReactNoop.render();
@@ -53,5 +59,47 @@ describe('ReactComponent', function() {
// console.log('Done');
});
+ it('should render a coroutine', function() {
+
+ function Continuation({ isSame }) {
+ return {isSame ? 'foo==bar' : 'foo!=bar'};
+ }
+
+ // An alternative API could mark Continuation as something that needs
+ // yielding. E.g. Continuation.yieldType = 123;
+ function Child({ bar }) {
+ return ReactCoroutine.createYield({
+ bar: bar,
+ }, Continuation, null);
+ }
+
+ function Indirection() {
+ return [, ];
+ }
+
+ function HandleYields(props, yields) {
+ return yields.map(y =>
+
+ );
+ }
+
+ // An alternative API could mark Parent as something that needs
+ // yielding. E.g. Parent.handler = HandleYields;
+ function Parent(props) {
+ return ReactCoroutine.createCoroutine(
+ props.children,
+ HandleYields,
+ props
+ );
+ }
+
+ function App() {
+ return ;
+ }
+
+ ReactNoop.render();
+ ReactNoop.flush();
+
+ });
});
diff --git a/src/renderers/shared/fiber/ReactChildFiber.js b/src/renderers/shared/fiber/ReactChildFiber.js
new file mode 100644
index 0000000000000..bbb666cab0903
--- /dev/null
+++ b/src/renderers/shared/fiber/ReactChildFiber.js
@@ -0,0 +1,129 @@
+/**
+ * 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 ReactChildFiber
+ * @flow
+ */
+
+'use strict';
+
+import type { ReactCoroutine, ReactYield } from 'ReactCoroutine';
+import type { Fiber } from 'ReactFiber';
+
+import type { ReactNodeList } from 'ReactTypes';
+
+var {
+ REACT_ELEMENT_TYPE,
+} = require('ReactElement');
+var {
+ REACT_COROUTINE_TYPE,
+ REACT_YIELD_TYPE,
+} = require('ReactCoroutine');
+
+var ReactFiber = require('ReactFiber');
+var ReactReifiedYield = require('ReactReifiedYield');
+
+function createSubsequentChild(parent : Fiber, previousSibling : Fiber, newChildren) : Fiber {
+ if (typeof newChildren !== 'object' || newChildren === null) {
+ return previousSibling;
+ }
+
+ switch (newChildren.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ const element = (newChildren : ReactElement);
+ const child = ReactFiber.createFiberFromElement(element);
+ previousSibling.sibling = child;
+ child.parent = parent;
+ return child;
+ }
+
+ case REACT_COROUTINE_TYPE: {
+ const coroutine = (newChildren : ReactCoroutine);
+ const child = ReactFiber.createFiberFromCoroutine(coroutine);
+ previousSibling.sibling = child;
+ child.parent = parent;
+ return child;
+ }
+
+ case REACT_YIELD_TYPE: {
+ const yieldNode = (newChildren : ReactYield);
+ const reifiedYield = ReactReifiedYield.createReifiedYield(yieldNode);
+ const child = ReactFiber.createFiberFromYield(yieldNode);
+ child.output = reifiedYield;
+ previousSibling.sibling = child;
+ child.parent = parent;
+ return child;
+ }
+ }
+
+ if (Array.isArray(newChildren)) {
+ let prev : Fiber = previousSibling;
+ for (var i = 0; i < newChildren.length; i++) {
+ prev = createSubsequentChild(parent, prev, newChildren[i]);
+ }
+ return prev;
+ } else {
+ console.log('Unknown child', newChildren);
+ return previousSibling;
+ }
+}
+
+function createFirstChild(parent, newChildren) {
+ if (typeof newChildren !== 'object' || newChildren === null) {
+ return null;
+ }
+
+ switch (newChildren.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ const element = (newChildren : ReactElement);
+ const child = ReactFiber.createFiberFromElement(element);
+ child.parent = parent;
+ return child;
+ }
+
+ case REACT_COROUTINE_TYPE: {
+ const coroutine = (newChildren : ReactCoroutine);
+ const child = ReactFiber.createFiberFromCoroutine(coroutine);
+ child.parent = parent;
+ return child;
+ }
+
+ case REACT_YIELD_TYPE: {
+ // A yield results in a fragment fiber whose output is the continuation.
+ // TODO: When there is only a single child, we can optimize this to avoid
+ // the fragment.
+ const yieldNode = (newChildren : ReactYield);
+ const reifiedYield = ReactReifiedYield.createReifiedYield(yieldNode);
+ const child = ReactFiber.createFiberFromYield(yieldNode);
+ child.output = reifiedYield;
+ child.parent = parent;
+ return child;
+ }
+ }
+
+ if (Array.isArray(newChildren)) {
+ var first : ?Fiber = null;
+ var prev : ?Fiber = null;
+ for (var i = 0; i < newChildren.length; i++) {
+ if (prev == null) {
+ prev = createFirstChild(parent, newChildren[i]);
+ first = prev;
+ } else {
+ prev = createSubsequentChild(parent, prev, newChildren[i]);
+ }
+ }
+ return first;
+ } else {
+ console.log('Unknown child', newChildren);
+ return null;
+ }
+}
+
+exports.reconcileChildFibers = function(parent : Fiber, firstChild : ?Fiber, newChildren : ReactNodeList) : ?Fiber {
+ return createFirstChild(parent, newChildren);
+};
diff --git a/src/renderers/shared/fiber/ReactFiber.js b/src/renderers/shared/fiber/ReactFiber.js
index 9e99e8998c6ba..eb17068b6c024 100644
--- a/src/renderers/shared/fiber/ReactFiber.js
+++ b/src/renderers/shared/fiber/ReactFiber.js
@@ -12,31 +12,47 @@
'use strict';
-type StateNode = {};
-type EffectHandler = () => void;
-type EffectTag = number;
+var ReactTypesOfWork = require('ReactTypesOfWork');
+var {
+ IndeterminateComponent,
+ ClassComponent,
+ HostComponent,
+ CoroutineComponent,
+ YieldComponent,
+} = ReactTypesOfWork;
+
+var ReactElement = require('ReactElement');
+
+import type { ReactCoroutine, ReactYield } from 'ReactCoroutine';
export type Fiber = {
+ // Tag identifying the type of fiber.
tag: number,
- parent: ?Fiber,
+ // Singly Linked List Tree Structure.
+ parent: ?Fiber, // Consider a regenerated temporary parent stack instead.
child: ?Fiber,
sibling: ?Fiber,
- input: ?Object,
- output: ?Object,
+ // Input is the data coming into process this fiber. Arguments.
+ input: any, // This type will be more specific once we overload the tag.
+ // Output is the return value of this fiber, or a linked list of return values
+ // if this returns multiple values. Such as a fragment.
+ output: any, // This type will be more specific once we overload the tag.
- handler: EffectHandler,
- handlerTag: EffectTag,
+ // Used by multi-stage coroutines.
+ stage: number, // Consider reusing the tag field instead.
+ // This will be used to quickly determine if a subtree has no pending changes.
hasPendingChanges: bool,
- stateNode: StateNode,
+ // The local state associated with this fiber.
+ stateNode: ?Object,
};
-module.exports = function(tag : number) : Fiber {
+var createFiber = function(tag : number) : Fiber {
return {
tag: tag,
@@ -48,12 +64,53 @@ module.exports = function(tag : number) : Fiber {
input: null,
output: null,
- handler: function() {},
- handlerTag: 0,
+ stage: 0,
hasPendingChanges: true,
- stateNode: {},
+ stateNode: null,
};
};
+
+function shouldConstruct(Component) {
+ return !!(Component.prototype && Component.prototype.isReactComponent);
+}
+
+exports.createFiberFromElement = function(element : ReactElement) {
+ const fiber = exports.createFiberFromElementType(element.type);
+ if (typeof element.type === 'object') {
+ // Hacky McHack
+ element = ReactElement(fiber.input, null, element.ref, null, null, null, element.props);
+ }
+ fiber.input = element;
+ return fiber;
+};
+
+exports.createFiberFromElementType = function(type : mixed) {
+ let fiber;
+ if (typeof type === 'function') {
+ fiber = shouldConstruct(type) ?
+ createFiber(ClassComponent) :
+ createFiber(IndeterminateComponent);
+ } else if (typeof type === 'string') {
+ fiber = createFiber(HostComponent);
+ } 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.createFiberFromCoroutine = function(coroutine : ReactCoroutine) {
+ const fiber = createFiber(CoroutineComponent);
+ fiber.input = coroutine;
+ return fiber;
+};
+
+exports.createFiberFromYield = function(yieldNode : ReactYield) {
+ const fiber = createFiber(YieldComponent);
+ return fiber;
+};
diff --git a/src/renderers/shared/fiber/ReactFiberBeginWork.js b/src/renderers/shared/fiber/ReactFiberBeginWork.js
new file mode 100644
index 0000000000000..19026242f9ec3
--- /dev/null
+++ b/src/renderers/shared/fiber/ReactFiberBeginWork.js
@@ -0,0 +1,134 @@
+/**
+ * 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 ReactFiberBeginWork
+ * @flow
+ */
+
+'use strict';
+
+import type { ReactCoroutine } from 'ReactCoroutine';
+import type { Fiber } from 'ReactFiber';
+
+var ReactChildFiber = require('ReactChildFiber');
+var ReactTypesOfWork = require('ReactTypesOfWork');
+var {
+ IndeterminateComponent,
+ FunctionalComponent,
+ ClassComponent,
+ HostComponent,
+ CoroutineComponent,
+ YieldComponent,
+} = ReactTypesOfWork;
+
+function getElement(unitOfWork) : ReactElement {
+ var element = unitOfWork.input;
+ if (!element) {
+ throw new Error('Should be resolved by now');
+ }
+ return (element : ReactElement);
+}
+
+function updateFunctionalComponent(unitOfWork) {
+ var element = getElement(unitOfWork);
+ var fn = element.type;
+ var props = element.props;
+ console.log('perform work on:', fn.name);
+ var nextChildren = fn(props);
+
+ unitOfWork.child = ReactChildFiber.reconcileChildFibers(
+ unitOfWork,
+ unitOfWork.child,
+ nextChildren
+ );
+}
+
+function updateHostComponent(unitOfWork) {
+ var element = getElement(unitOfWork);
+ console.log('host component', element.type, typeof element.props.children === 'string' ? element.props.children : '');
+
+ var nextChildren = element.props.children;
+ unitOfWork.child = ReactChildFiber.reconcileChildFibers(
+ unitOfWork,
+ unitOfWork.child,
+ nextChildren
+ );
+}
+
+function mountIndeterminateComponent(unitOfWork) {
+ var element = getElement(unitOfWork);
+ var fn = element.type;
+ var props = element.props;
+ 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
+ unitOfWork.tag = ClassComponent;
+ } else {
+ console.log('performed work on fn:', fn.name);
+ // Proceed under the assumption that this is a functional component
+ unitOfWork.tag = FunctionalComponent;
+ }
+ unitOfWork.child = ReactChildFiber.reconcileChildFibers(
+ unitOfWork,
+ unitOfWork.child,
+ value
+ );
+}
+
+function updateCoroutineComponent(unitOfWork) {
+ var coroutine = (unitOfWork.input : ?ReactCoroutine);
+ if (!coroutine) {
+ throw new Error('Should be resolved by now');
+ }
+ console.log('begin coroutine', coroutine.handler.name);
+ unitOfWork.child = ReactChildFiber.reconcileChildFibers(
+ unitOfWork,
+ unitOfWork.child,
+ coroutine.children
+ );
+}
+
+function beginWork(unitOfWork : Fiber) : ?Fiber {
+ switch (unitOfWork.tag) {
+ case IndeterminateComponent:
+ mountIndeterminateComponent(unitOfWork);
+ break;
+ case FunctionalComponent:
+ updateFunctionalComponent(unitOfWork);
+ break;
+ case ClassComponent:
+ console.log('class component', unitOfWork.input.type.name);
+ break;
+ case HostComponent:
+ updateHostComponent(unitOfWork);
+ break;
+ case CoroutineComponent:
+ // Reset the stage to zero.
+ unitOfWork.stage = 0;
+ updateCoroutineComponent(unitOfWork);
+ // This doesn't take arbitrary time so we could synchronously just begin
+ // eagerly do the work of unitOfWork.child as an optimization.
+ if (unitOfWork.child) {
+ return beginWork(unitOfWork.child);
+ }
+ break;
+ case YieldComponent:
+ // A yield component is just a placeholder, we can just run through the
+ // next one immediately.
+ if (unitOfWork.sibling) {
+ return beginWork(unitOfWork.sibling);
+ }
+ return null;
+ default:
+ throw new Error('Unknown unit of work tag');
+ }
+ return unitOfWork.child;
+}
+
+exports.beginWork = beginWork;
diff --git a/src/renderers/shared/fiber/ReactFiberCompleteWork.js b/src/renderers/shared/fiber/ReactFiberCompleteWork.js
new file mode 100644
index 0000000000000..827075f004312
--- /dev/null
+++ b/src/renderers/shared/fiber/ReactFiberCompleteWork.js
@@ -0,0 +1,121 @@
+/**
+ * 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 ReactFiberCompleteWork
+ * @flow
+ */
+
+'use strict';
+
+import type { ReactCoroutine } from 'ReactCoroutine';
+import type { Fiber } from 'ReactFiber';
+
+import type { ReifiedYield } from 'ReactReifiedYield';
+
+var ReactChildFiber = require('ReactChildFiber');
+var ReactTypesOfWork = require('ReactTypesOfWork');
+var {
+ IndeterminateComponent,
+ FunctionalComponent,
+ ClassComponent,
+ HostComponent,
+ CoroutineComponent,
+ YieldComponent,
+} = ReactTypesOfWork;
+
+function transferOutput(child : ?Fiber, parent : 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.
+ parent.output = (child && !child.sibling) ? child.output : child;
+}
+
+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 handleCoroutine(unitOfWork : Fiber) {
+ var coroutine = (unitOfWork.input : ?ReactCoroutine);
+ if (!coroutine) {
+ throw new Error('Should be resolved by now');
+ }
+
+ if (unitOfWork.stage === 0) {
+ // 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.
+ // TODO: If we end up not using multi stage coroutines, we could also reuse
+ // the tag field to switch between the two stages.
+ unitOfWork.stage = 1;
+
+ // Build up the yields.
+ // TODO: Compare this to a generator or opaque helpers like Children.
+ var yields : Array = [];
+ var child = unitOfWork.child;
+ while (child) {
+ recursivelyFillYields(yields, child.output);
+ child = child.sibling;
+ }
+ var fn = coroutine.handler;
+ var props = coroutine.props;
+ var nextChildren = fn(props, yields);
+
+ unitOfWork.stateNode = ReactChildFiber.reconcileChildFibers(
+ unitOfWork,
+ unitOfWork.stateNode,
+ nextChildren
+ );
+ return unitOfWork.stateNode;
+ } else {
+ // The coroutine is now complete.
+ transferOutput(unitOfWork.stateNode, unitOfWork);
+ return null;
+ }
+}
+
+exports.completeWork = function(unitOfWork : Fiber) : ?Fiber {
+ switch (unitOfWork.tag) {
+ case FunctionalComponent:
+ console.log('/functional component', unitOfWork.input.type.name);
+ transferOutput(unitOfWork.child, unitOfWork);
+ break;
+ case ClassComponent:
+ console.log('/class component', unitOfWork.input.type.name);
+ transferOutput(unitOfWork.child, unitOfWork);
+ break;
+ case HostComponent:
+ console.log('/host component', unitOfWork.input.type);
+ break;
+ case CoroutineComponent:
+ console.log('/coroutine component', unitOfWork.input.handler.name);
+ return handleCoroutine(unitOfWork);
+ case YieldComponent:
+ // Does nothing.
+ break;
+
+ // 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 null;
+};
diff --git a/src/renderers/shared/fiber/ReactFiberFunctionalComponent.js b/src/renderers/shared/fiber/ReactFiberFunctionalComponent.js
deleted file mode 100644
index 8c5ac48f8c06f..0000000000000
--- a/src/renderers/shared/fiber/ReactFiberFunctionalComponent.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * 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;
-};
diff --git a/src/renderers/shared/fiber/ReactFiberReconciler.js b/src/renderers/shared/fiber/ReactFiberReconciler.js
index 3ef59bdb5e163..4a13a6531bca5 100644
--- a/src/renderers/shared/fiber/ReactFiberReconciler.js
+++ b/src/renderers/shared/fiber/ReactFiberReconciler.js
@@ -13,14 +13,9 @@
'use strict';
import type { Fiber } from 'ReactFiber';
-var ReactFiberFunctionalComponent = require('ReactFiberFunctionalComponent');
-
-var ReactTypesOfWork = require('ReactTypesOfWork');
-var {
- FunctionalComponent,
- ClassComponent,
- HostComponent,
-} = ReactTypesOfWork;
+var ReactFiber = require('ReactFiber');
+var { beginWork } = require('ReactFiberBeginWork');
+var { completeWork } = require('ReactFiberCompleteWork');
type ReactHostElement = {
type: T,
@@ -54,16 +49,34 @@ module.exports = function(config : HostConfig) : Reconciler {
let nextUnitOfWork : ?Fiber = null;
- function performUnitOfWork(unit : Fiber) : ?Fiber {
- switch (unit.tag) {
- case FunctionalComponent:
- return ReactFiberFunctionalComponent.performWork(unit);
- case ClassComponent:
- break;
- case HostComponent:
- break;
+ function completeUnitOfWork(unitOfWork : Fiber) : ?Fiber {
+ while (true) {
+ var next = completeWork(unitOfWork);
+ if (next) {
+ // If completing this work spawned new work, do that next.
+ return next;
+ } else if (unitOfWork.sibling) {
+ // If there is more work to do in this parent, do that next.
+ return unitOfWork.sibling;
+ } else if (unitOfWork.parent) {
+ // If there's no more work in this parent. Complete the parent.
+ unitOfWork = unitOfWork.parent;
+ } else {
+ // If we're at the root, there's no more work to do.
+ return null;
+ }
+ }
+ }
+
+ function performUnitOfWork(unitOfWork : Fiber) : ?Fiber {
+ var next = beginWork(unitOfWork);
+ if (next) {
+ // If this spawns new work, do that next.
+ return next;
+ } else {
+ // Otherwise, complete the current work.
+ return completeUnitOfWork(unitOfWork);
}
- return null;
}
function performLowPriWork(deadline : Deadline) {
@@ -100,7 +113,7 @@ module.exports = function(config : HostConfig) : Reconciler {
ensureLowPriIsScheduled();
- nextUnitOfWork = ReactFiberFunctionalComponent.createFiber(element);
+ nextUnitOfWork = ReactFiber.createFiberFromElement(element);
return {};
},
diff --git a/src/renderers/shared/fiber/ReactReifiedYield.js b/src/renderers/shared/fiber/ReactReifiedYield.js
new file mode 100644
index 0000000000000..a47887bc56ba1
--- /dev/null
+++ b/src/renderers/shared/fiber/ReactReifiedYield.js
@@ -0,0 +1,37 @@
+/**
+ * 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 ReactReifiedYield
+ * @flow
+ */
+
+'use strict';
+
+import type { ReactYield } from 'ReactCoroutine';
+import type { Fiber } from 'ReactFiber';
+
+var ReactFiber = require('ReactFiber');
+
+export type ReifiedYield = { continuation: Fiber, props: Object };
+
+exports.createReifiedYield = function(yieldNode : ReactYield) : ReifiedYield {
+ var fiber = ReactFiber.createFiberFromElementType(yieldNode.continuation);
+ // Hacky way to store the continuation
+ fiber.input = yieldNode.continuation;
+ return {
+ continuation: fiber,
+ props: yieldNode.props,
+ };
+};
+
+exports.createUpdatedReifiedYield = function(previousYield : ReifiedYield, yieldNode : ReactYield) : ReifiedYield {
+ return {
+ continuation: previousYield.continuation,
+ props: yieldNode.props,
+ };
+};
diff --git a/src/renderers/shared/fiber/ReactStateNode.js b/src/renderers/shared/fiber/ReactStateNode.js
deleted file mode 100644
index c7ea45b3b4064..0000000000000
--- a/src/renderers/shared/fiber/ReactStateNode.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * 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,
- };
-};
diff --git a/src/renderers/shared/fiber/ReactTypesOfWork.js b/src/renderers/shared/fiber/ReactTypesOfWork.js
index 5dda9346102f0..5009cfa6bcb5b 100644
--- a/src/renderers/shared/fiber/ReactTypesOfWork.js
+++ b/src/renderers/shared/fiber/ReactTypesOfWork.js
@@ -13,9 +13,12 @@
'use strict';
var TypesOfWork = {
+ IndeterminateComponent: 0, // Before we know whether it is functional or class
FunctionalComponent: 1,
ClassComponent: 2,
HostComponent: 3,
+ CoroutineComponent: 4,
+ YieldComponent: 5,
};
module.exports = TypesOfWork;
diff --git a/src/renderers/shared/fiber/isomorphic/ReactCoroutine.js b/src/renderers/shared/fiber/isomorphic/ReactCoroutine.js
new file mode 100644
index 0000000000000..a03934ea56fcd
--- /dev/null
+++ b/src/renderers/shared/fiber/isomorphic/ReactCoroutine.js
@@ -0,0 +1,109 @@
+/**
+ * Copyright 2014-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 ReactCoroutine
+ * @flow
+ */
+
+'use strict';
+
+import type { ReactNodeList } from 'ReactTypes';
+
+// The Symbol used to tag the special React types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var REACT_COROUTINE_TYPE =
+ (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.coroutine')) ||
+ 0xeac8;
+
+var REACT_YIELD_TYPE =
+ (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.yield')) ||
+ 0xeac9;
+
+type ReifiedYield = { continuation: Object, props: Object };
+type CoroutineHandler = (props: T, yields: Array) => ReactNodeList;
+
+export type ReactCoroutine = {
+ $$typeof: Symbol | number,
+ key: ?string,
+ children: any,
+ // This should be a more specific CoroutineHandler
+ handler: (props: any, yields: Array) => ReactNodeList,
+ props: mixed,
+};
+export type ReactYield = {
+ $$typeof: Symbol | number,
+ key: ?string,
+ props: Object,
+ continuation: mixed
+};
+
+exports.createCoroutine = function(children : mixed, handler : CoroutineHandler, props : T, key : ?string = null) : ReactCoroutine {
+ var coroutine = {
+ // This tag allow us to uniquely identify this as a React Coroutine
+ $$typeof: REACT_COROUTINE_TYPE,
+ key: key == null ? null : '' + key,
+ children: children,
+ handler: handler,
+ props: props,
+ };
+
+ if (__DEV__) {
+ // TODO: Add _store property for marking this as validated.
+ if (Object.freeze) {
+ Object.freeze(coroutine.props);
+ Object.freeze(coroutine);
+ }
+ }
+
+ return coroutine;
+};
+
+exports.createYield = function(props : mixed, continuation : mixed, key : ?string = null) {
+ var yieldNode = {
+ // This tag allow us to uniquely identify this as a React Yield
+ $$typeof: REACT_YIELD_TYPE,
+ key: key == null ? null : '' + key,
+ props: props,
+ continuation: continuation,
+ };
+
+ if (__DEV__) {
+ // TODO: Add _store property for marking this as validated.
+ if (Object.freeze) {
+ Object.freeze(yieldNode.props);
+ Object.freeze(yieldNode);
+ }
+ }
+
+ return yieldNode;
+};
+
+/**
+ * Verifies the object is a coroutine object.
+ */
+exports.isCoroutine = function(object : mixed) : boolean {
+ return (
+ typeof object === 'object' &&
+ object !== null &&
+ object.$$typeof === REACT_COROUTINE_TYPE
+ );
+};
+
+/**
+ * Verifies the object is a yield object.
+ */
+exports.isYield = function(object : mixed) : boolean {
+ return (
+ typeof object === 'object' &&
+ object !== null &&
+ object.$$typeof === REACT_YIELD_TYPE
+ );
+};
+
+exports.REACT_YIELD_TYPE = REACT_YIELD_TYPE;
+exports.REACT_COROUTINE_TYPE = REACT_COROUTINE_TYPE;
diff --git a/src/renderers/shared/fiber/isomorphic/ReactTypes.js b/src/renderers/shared/fiber/isomorphic/ReactTypes.js
new file mode 100644
index 0000000000000..4c8c69571c222
--- /dev/null
+++ b/src/renderers/shared/fiber/isomorphic/ReactTypes.js
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2014-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 ReactTypes
+ * @flow
+ */
+
+'use strict';
+
+import type { ReactCoroutine, ReactYield } from 'ReactCoroutine';
+
+export type ReactNode = ReactElement | ReactCoroutine | ReactYield | ReactText | ReactFragment;
+
+export type ReactFragment = ReactEmpty | Iterable;
+
+export type ReactNodeList = ReactEmpty | ReactNode;
+
+export type ReactText = string | number;
+
+export type ReactEmpty = null | void | boolean;
diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactComponent-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactComponent-test.js
index f97ae25c09776..cd0c13d6e5f92 100644
--- a/src/renderers/shared/stack/reconciler/__tests__/ReactComponent-test.js
+++ b/src/renderers/shared/stack/reconciler/__tests__/ReactComponent-test.js
@@ -280,14 +280,8 @@ describe('ReactComponent', function() {
'or a class/function (for composite components) but got: null.'
);
- var Z = {};
- expect(() => ReactTestUtils.renderIntoDocument()).toThrowError(
- 'Element type is invalid: expected a string (for built-in components) ' +
- 'or a class/function (for composite components) but got: object.'
- );
-
// One warning for each element creation
- expect(console.error.calls.count()).toBe(3);
+ expect(console.error.calls.count()).toBe(2);
});
});