@samitouri / QOS-React-2 / commits / fd4f74ef95

Coroutines

Sebastian Markbage committed May 26, 2016 at 18:13 UTC fd4f74ef955b6d627af8a9fc55a61c828564a67e
12 files changed +471 -90
src/isomorphic/classic/element/ReactElement.js
+2
@@ -398,4 +398,6 @@ ReactElement.isValidElement = function(object) {
398 );
399 };
400
401 +ReactElement.REACT_ELEMENT_TYPE = REACT_ELEMENT_TYPE;
402 +
403 module.exports = ReactElement;
src/isomorphic/classic/element/ReactElementValidator.js
+2 -1
@@ -184,7 +184,8 @@ function validatePropTypes(element) {
184 var ReactElementValidator = {
185
186 createElement: function(type, props, children) {
187 - var validType = typeof type === 'string' || typeof type === 'function';
187 + var validType = typeof type === 'string' || typeof type === 'function' ||
188 + (type !== null && typeof type === 'object');
189 // We warn in this case but don't throw. We expect the element creation to
190 // succeed and there will likely be errors in render.
191 warning(
src/renderers/noop/__tests__/ReactNoop-test.js
+50 -5
@@ -13,14 +13,17 @@
13
14 var React;
15 var ReactNoop;
16 +var ReactCoroutine;
17
18 describe('ReactComponent', function() {
19 beforeEach(function() {
20 React = require('React');
21 ReactNoop = require('ReactNoop');
22 + ReactCoroutine = require('ReactCoroutine');
23 + spyOn(console, 'log');
24 });
25
23 - /*it('should render a simple component', function() {
26 + it('should render a simple component', function() {
27
28 function Bar() {
29 return <div>Hello World</div>;
@@ -33,7 +36,7 @@ describe('ReactComponent', function() {
36 ReactNoop.render(<Foo />);
37 ReactNoop.flush();
38
36 - });*/
39 + });
40
41 it('should render a simple component, in steps if needed', function() {
42
@@ -49,12 +52,54 @@ describe('ReactComponent', function() {
52 }
53
54 ReactNoop.render(<Foo />);
52 - console.log('Nothing done');
55 + // console.log('Nothing done');
56 ReactNoop.flushLowPri(7);
54 - console.log('Yield');
57 + // console.log('Yield');
58 ReactNoop.flushLowPri(50);
56 - console.log('Done');
59 + // console.log('Done');
60 });
61
62 + it('should render a coroutine', function() {
63 +
64 + function Continuation({ isSame }) {
65 + return <span>{isSame ? 'foo==bar' : 'foo!=bar'}</span>;
66 + }
67 +
68 + // An alternative API could mark Continuation as something that needs
69 + // yielding. E.g. Continuation.yieldType = 123;
70 + function Child({ bar }) {
71 + return ReactCoroutine.createYield({
72 + bar: bar,
73 + }, Continuation, null);
74 + }
75 +
76 + function Indirection() {
77 + return [<Child bar={true} />, <Child bar={false} />];
78 + }
79 +
80 + function HandleYields(props, yields) {
81 + return yields.map(y =>
82 + <y.continuation isSame={props.foo === y.props.bar} />
83 + );
84 + }
85 +
86 + // An alternative API could mark Parent as something that needs
87 + // yielding. E.g. Parent.handler = HandleYields;
88 + function Parent(props) {
89 + return ReactCoroutine.createCoroutine(
90 + props.children,
91 + HandleYields,
92 + props
93 + );
94 + }
95 +
96 + function App() {
97 + return <div><Parent foo={true}><Indirection /></Parent></div>;
98 + }
99 +
100 + ReactNoop.render(<App />);
101 + ReactNoop.flush();
102 +
103 + });
104
105 });
src/renderers/shared/fiber/ReactChildFiber.js
+70 -27
@@ -12,33 +12,53 @@
12
13 'use strict';
14
15 +import type { ReactCoroutine, ReactYield } from 'ReactCoroutine';
16 import type { Fiber } from 'ReactFiber';
17
17 -var ReactElement = require('ReactElement');
18 +import type { ReactNodeList } from 'ReactTypes';
19
19 -var ReactFiber = require('ReactFiber');
20 -
21 -type ReactNode = ReactElement | ReactFragment | ReactText;
22 -
23 -type ReactFragment = Iterable<ReactNode | ReactEmpty>;
24 -
25 -type ReactNodeList = ReactNode | ReactEmpty;
20 +var {
21 + REACT_ELEMENT_TYPE,
22 +} = require('ReactElement');
23 +var {
24 + REACT_COROUTINE_TYPE,
25 + REACT_YIELD_TYPE,
26 +} = require('ReactCoroutine');
27
27 -type ReactText = string | number;
28 -
29 -type ReactEmpty = null | void | boolean;
28 +var ReactFiber = require('ReactFiber');
29 +var ReactReifiedYield = require('ReactReifiedYield');
30
31 function createSubsequentChild(parent : Fiber, previousSibling : Fiber, newChildren) : Fiber {
32 if (typeof newChildren !== 'object' || newChildren === null) {
33 return previousSibling;
34 }
35
36 - if (ReactElement.isValidElement(newChildren)) {
37 - var element = (newChildren : ReactElement);
38 - var child = ReactFiber.createFiberFromElement(element);
39 - previousSibling.sibling = child;
40 - child.parent = parent;
41 - return child;
36 + switch (newChildren.$$typeof) {
37 + case REACT_ELEMENT_TYPE: {
38 + const element = (newChildren : ReactElement);
39 + const child = ReactFiber.createFiberFromElement(element);
40 + previousSibling.sibling = child;
41 + child.parent = parent;
42 + return child;
43 + }
44 +
45 + case REACT_COROUTINE_TYPE: {
46 + const coroutine = (newChildren : ReactCoroutine);
47 + const child = ReactFiber.createFiberFromCoroutine(coroutine);
48 + previousSibling.sibling = child;
49 + child.parent = parent;
50 + return child;
51 + }
52 +
53 + case REACT_YIELD_TYPE: {
54 + const yieldNode = (newChildren : ReactYield);
55 + const reifiedYield = ReactReifiedYield.createReifiedYield(yieldNode);
56 + const child = ReactFiber.createFiberFromYield(yieldNode);
57 + child.output = reifiedYield;
58 + previousSibling.sibling = child;
59 + child.parent = parent;
60 + return child;
61 + }
62 }
63
64 if (Array.isArray(newChildren)) {
@@ -48,39 +68,62 @@ function createSubsequentChild(parent : Fiber, previousSibling : Fiber, newChild
68 }
69 return prev;
70 } else {
71 + console.log('Unknown child', newChildren);
72 return previousSibling;
73 }
74 }
75
76 function createFirstChild(parent, newChildren) {
77 if (typeof newChildren !== 'object' || newChildren === null) {
57 - parent.child = null;
78 return null;
79 }
80
61 - if (ReactElement.isValidElement(newChildren)) {
62 - var element = (newChildren : ReactElement);
63 - var child = ReactFiber.createFiberFromElement(element);
64 - parent.child = child;
65 - child.parent = parent;
66 - return child;
81 + switch (newChildren.$$typeof) {
82 + case REACT_ELEMENT_TYPE: {
83 + const element = (newChildren : ReactElement);
84 + const child = ReactFiber.createFiberFromElement(element);
85 + child.parent = parent;
86 + return child;
87 + }
88 +
89 + case REACT_COROUTINE_TYPE: {
90 + const coroutine = (newChildren : ReactCoroutine);
91 + const child = ReactFiber.createFiberFromCoroutine(coroutine);
92 + child.parent = parent;
93 + return child;
94 + }
95 +
96 + case REACT_YIELD_TYPE: {
97 + // A yield results in a fragment fiber whose output is the continuation.
98 + // TODO: When there is only a single child, we can optimize this to avoid
99 + // the fragment.
100 + const yieldNode = (newChildren : ReactYield);
101 + const reifiedYield = ReactReifiedYield.createReifiedYield(yieldNode);
102 + const child = ReactFiber.createFiberFromYield(yieldNode);
103 + child.output = reifiedYield;
104 + child.parent = parent;
105 + return child;
106 + }
107 }
108
109 if (Array.isArray(newChildren)) {
110 + var first : ?Fiber = null;
111 var prev : ?Fiber = null;
112 for (var i = 0; i < newChildren.length; i++) {
113 if (prev == null) {
114 prev = createFirstChild(parent, newChildren[i]);
115 + first = prev;
116 } else {
117 prev = createSubsequentChild(parent, prev, newChildren[i]);
118 }
119 }
120 + return first;
121 } else {
79 - parent.child = null;
122 + console.log('Unknown child', newChildren);
123 return null;
124 }
125 }
126
84 -exports.reconcileChildFibers = function(parent : Fiber, newChildren : ReactNodeList) : void {
85 - createFirstChild(parent, newChildren);
127 +exports.reconcileChildFibers = function(parent : Fiber, firstChild : ?Fiber, newChildren : ReactNodeList) : ?Fiber {
128 + return createFirstChild(parent, newChildren);
129 };
src/renderers/shared/fiber/ReactFiber.js
+53 -17
@@ -17,30 +17,42 @@ var {
17 IndeterminateComponent,
18 ClassComponent,
19 HostComponent,
20 + CoroutineComponent,
21 + YieldComponent,
22 } = ReactTypesOfWork;
23
22 -type StateNode = {};
23 -type EffectHandler = () => void;
24 -type EffectTag = number;
24 +var ReactElement = require('ReactElement');
25 +
26 +import type { ReactCoroutine, ReactYield } from 'ReactCoroutine';
27
28 export type Fiber = {
29
30 + // Tag identifying the type of fiber.
31 tag: number,
32
30 - parent: ?Fiber,
33 + // Singly Linked List Tree Structure.
34 + parent: ?Fiber, // Consider a regenerated temporary parent stack instead.
35 child: ?Fiber,
36 sibling: ?Fiber,
37
34 - input: ?Object,
35 - output: ?Object,
38 + // Input is the data coming into process this fiber. Arguments.
39 + input: any, // This type will be more specific once we overload the tag.
40 + // Output is the return value of this fiber, or a linked list of return values
41 + // if this returns multiple values. Such as a fragment.
42 + output: any, // This type will be more specific once we overload the tag.
43 +
44 + // Used by multi-stage coroutines.
45 + stage: number, // Consider reusing the tag field instead.
46
47 + // This will be used to quickly determine if a subtree has no pending changes.
48 hasPendingChanges: bool,
49
39 - stateNode: StateNode,
50 + // The local state associated with this fiber.
51 + stateNode: ?Object,
52
53 };
54
43 -var createFiber = function(tag : number, handlerTag : number) : Fiber {
55 +var createFiber = function(tag : number) : Fiber {
56 return {
57
58 tag: tag,
@@ -52,9 +64,11 @@ var createFiber = function(tag : number, handlerTag : number) : Fiber {
64 input: null,
65 output: null,
66
67 + stage: 0,
68 +
69 hasPendingChanges: true,
70
57 - stateNode: {},
71 + stateNode: null,
72
73 };
74 };
@@ -64,17 +78,39 @@ function shouldConstruct(Component) {
78 }
79
80 exports.createFiberFromElement = function(element : ReactElement) {
81 + const fiber = exports.createFiberFromElementType(element.type);
82 + if (typeof element.type === 'object') {
83 + // Hacky McHack
84 + element = ReactElement(fiber.input, null, element.ref, null, null, null, element.props);
85 + }
86 + fiber.input = element;
87 + return fiber;
88 +};
89 +
90 +exports.createFiberFromElementType = function(type : mixed) {
91 let fiber;
68 - if (typeof element.type === 'function') {
69 - fiber = shouldConstruct(element.type) ?
70 - createFiber(ClassComponent, 0) :
71 - createFiber(IndeterminateComponent, 0);
72 - } else if (typeof element.type === 'string') {
73 - fiber = createFiber(HostComponent, 1);
92 + if (typeof type === 'function') {
93 + fiber = shouldConstruct(type) ?
94 + createFiber(ClassComponent) :
95 + createFiber(IndeterminateComponent);
96 + } else if (typeof type === 'string') {
97 + fiber = createFiber(HostComponent);
98 + } else if (typeof type === 'object' && type !== null) {
99 + // Currently assumed to be a continuation and therefore is a fiber already.
100 + fiber = type;
101 } else {
75 - throw new Error('Unknown component type: ' + typeof element.type);
102 + throw new Error('Unknown component type: ' + typeof type);
103 }
104 + return fiber;
105 +};
106
78 - fiber.input = element;
107 +exports.createFiberFromCoroutine = function(coroutine : ReactCoroutine) {
108 + const fiber = createFiber(CoroutineComponent);
109 + fiber.input = coroutine;
110 + return fiber;
111 +};
112 +
113 +exports.createFiberFromYield = function(yieldNode : ReactYield) {
114 + const fiber = createFiber(YieldComponent);
115 return fiber;
116 };
src/renderers/shared/fiber/ReactFiberBeginWork.js
+44 -7
@@ -12,6 +12,7 @@
12
13 'use strict';
14
15 +import type { ReactCoroutine } from 'ReactCoroutine';
16 import type { Fiber } from 'ReactFiber';
17
18 var ReactChildFiber = require('ReactChildFiber');
@@ -21,6 +22,8 @@ var {
22 FunctionalComponent,
23 ClassComponent,
24 HostComponent,
25 + CoroutineComponent,
26 + YieldComponent,
27 } = ReactTypesOfWork;
28
29 function getElement(unitOfWork) : ReactElement {
@@ -38,19 +41,21 @@ function updateFunctionalComponent(unitOfWork) {
41 console.log('perform work on:', fn.name);
42 var nextChildren = fn(props);
43
41 - ReactChildFiber.reconcileChildFibers(
44 + unitOfWork.child = ReactChildFiber.reconcileChildFibers(
45 unitOfWork,
46 + unitOfWork.child,
47 nextChildren
48 );
49 }
50
51 function updateHostComponent(unitOfWork) {
52 var element = getElement(unitOfWork);
49 - console.log('host component', element.type);
53 + console.log('host component', element.type, typeof element.props.children === 'string' ? element.props.children : '');
54
55 var nextChildren = element.props.children;
52 - ReactChildFiber.reconcileChildFibers(
56 + unitOfWork.child = ReactChildFiber.reconcileChildFibers(
57 unitOfWork,
58 + unitOfWork.child,
59 nextChildren
60 );
61 }
@@ -69,13 +74,27 @@ function mountIndeterminateComponent(unitOfWork) {
74 // Proceed under the assumption that this is a functional component
75 unitOfWork.tag = FunctionalComponent;
76 }
72 - ReactChildFiber.reconcileChildFibers(
77 + unitOfWork.child = ReactChildFiber.reconcileChildFibers(
78 unitOfWork,
79 + unitOfWork.child,
80 value
81 );
82 }
83
78 -exports.beginWork = function(unitOfWork : Fiber) : ?Fiber {
84 +function updateCoroutineComponent(unitOfWork) {
85 + var coroutine = (unitOfWork.input : ?ReactCoroutine);
86 + if (!coroutine) {
87 + throw new Error('Should be resolved by now');
88 + }
89 + console.log('begin coroutine', coroutine.handler.name);
90 + unitOfWork.child = ReactChildFiber.reconcileChildFibers(
91 + unitOfWork,
92 + unitOfWork.child,
93 + coroutine.children
94 + );
95 +}
96 +
97 +function beginWork(unitOfWork : Fiber) : ?Fiber {
98 switch (unitOfWork.tag) {
99 case IndeterminateComponent:
100 mountIndeterminateComponent(unitOfWork);
@@ -84,14 +103,32 @@ exports.beginWork = function(unitOfWork : Fiber) : ?Fiber {
103 updateFunctionalComponent(unitOfWork);
104 break;
105 case ClassComponent:
87 - // $FlowFixMe
106 console.log('class component', unitOfWork.input.type.name);
107 break;
108 case HostComponent:
109 updateHostComponent(unitOfWork);
110 break;
111 + case CoroutineComponent:
112 + // Reset the stage to zero.
113 + unitOfWork.stage = 0;
114 + updateCoroutineComponent(unitOfWork);
115 + // This doesn't take arbitrary time so we could synchronously just begin
116 + // eagerly do the work of unitOfWork.child as an optimization.
117 + if (unitOfWork.child) {
118 + return beginWork(unitOfWork.child);
119 + }
120 + break;
121 + case YieldComponent:
122 + // A yield component is just a placeholder, we can just run through the
123 + // next one immediately.
124 + if (unitOfWork.sibling) {
125 + return beginWork(unitOfWork.sibling);
126 + }
127 + return null;
128 default:
129 throw new Error('Unknown unit of work tag');
130 }
131 return unitOfWork.child;
97 -};
132 +}
133 +
134 +exports.beginWork = beginWork;
src/renderers/shared/fiber/ReactFiberCompleteWork.js
+78 -3
@@ -12,8 +12,11 @@
12
13 'use strict';
14
15 +import type { ReactCoroutine } from 'ReactCoroutine';
16 import type { Fiber } from 'ReactFiber';
17
18 +import type { ReifiedYield } from 'ReactReifiedYield';
19 +
20 var ReactChildFiber = require('ReactChildFiber');
21 var ReactTypesOfWork = require('ReactTypesOfWork');
22 var {
@@ -21,22 +24,94 @@ var {
24 FunctionalComponent,
25 ClassComponent,
26 HostComponent,
27 + CoroutineComponent,
28 + YieldComponent,
29 } = ReactTypesOfWork;
30
31 +function transferOutput(child : ?Fiber, parent : Fiber) {
32 + // If we have a single result, we just pass that through as the output to
33 + // avoid unnecessary traversal. When we have multiple output, we just pass
34 + // the linked list of fibers that has the individual output values.
35 + parent.output = (child && !child.sibling) ? child.output : child;
36 +}
37 +
38 +function recursivelyFillYields(yields, output : ?Fiber | ?ReifiedYield) {
39 + if (!output) {
40 + // Ignore nulls etc.
41 + } else if (output.tag !== undefined) { // TODO: Fix this fragile duck test.
42 + // Detect if this is a fiber, if so it is a fragment result.
43 + // $FlowFixMe: Refinement issue.
44 + var item = (output : Fiber);
45 + do {
46 + recursivelyFillYields(yields, item.output);
47 + item = item.sibling;
48 + } while (item);
49 + } else {
50 + // $FlowFixMe: Refinement issue. If it is not a Fiber or null, it is a yield
51 + yields.push(output);
52 + }
53 +}
54 +
55 +function handleCoroutine(unitOfWork : Fiber) {
56 + var coroutine = (unitOfWork.input : ?ReactCoroutine);
57 + if (!coroutine) {
58 + throw new Error('Should be resolved by now');
59 + }
60 +
61 + if (unitOfWork.stage === 0) {
62 + // First step of the coroutine has completed. Now we need to do the second.
63 + // TODO: It would be nice to have a multi stage coroutine represented by a
64 + // single component, or at least tail call optimize nested ones.
65 + // TODO: If we end up not using multi stage coroutines, we could also reuse
66 + // the tag field to switch between the two stages.
67 + unitOfWork.stage = 1;
68 +
69 + // Build up the yields.
70 + // TODO: Compare this to a generator or opaque helpers like Children.
71 + var yields : Array<ReifiedYield> = [];
72 + var child = unitOfWork.child;
73 + while (child) {
74 + recursivelyFillYields(yields, child.output);
75 + child = child.sibling;
76 + }
77 + var fn = coroutine.handler;
78 + var props = coroutine.props;
79 + var nextChildren = fn(props, yields);
80 +
81 + unitOfWork.stateNode = ReactChildFiber.reconcileChildFibers(
82 + unitOfWork,
83 + unitOfWork.stateNode,
84 + nextChildren
85 + );
86 + return unitOfWork.stateNode;
87 + } else {
88 + // The coroutine is now complete.
89 + transferOutput(unitOfWork.stateNode, unitOfWork);
90 + return null;
91 + }
92 +}
93 +
94 exports.completeWork = function(unitOfWork : Fiber) : ?Fiber {
95 switch (unitOfWork.tag) {
96 case FunctionalComponent:
29 - // $FlowFixMe
97 console.log('/functional component', unitOfWork.input.type.name);
98 + transferOutput(unitOfWork.child, unitOfWork);
99 break;
100 case ClassComponent:
33 - // $FlowFixMe
101 console.log('/class component', unitOfWork.input.type.name);
102 + transferOutput(unitOfWork.child, unitOfWork);
103 break;
104 case HostComponent:
37 - // $FlowFixMe
105 console.log('/host component', unitOfWork.input.type);
106 break;
107 + case CoroutineComponent:
108 + console.log('/coroutine component', unitOfWork.input.handler.name);
109 + return handleCoroutine(unitOfWork);
110 + case YieldComponent:
111 + // Does nothing.
112 + break;
113 +
114 + // Error cases
115 case IndeterminateComponent:
116 throw new Error('An indeterminate component should have become determinate before completing.');
117 default:
src/renderers/shared/fiber/ReactReifiedYield.js new
+37
@@ -0,0 +1,37 @@
1 +/**
2 + * Copyright 2013-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule ReactReifiedYield
10 + * @flow
11 + */
12 +
13 +'use strict';
14 +
15 +import type { ReactYield } from 'ReactCoroutine';
16 +import type { Fiber } from 'ReactFiber';
17 +
18 +var ReactFiber = require('ReactFiber');
19 +
20 +export type ReifiedYield = { continuation: Fiber, props: Object };
21 +
22 +exports.createReifiedYield = function(yieldNode : ReactYield) : ReifiedYield {
23 + var fiber = ReactFiber.createFiberFromElementType(yieldNode.continuation);
24 + // Hacky way to store the continuation
25 + fiber.input = yieldNode.continuation;
26 + return {
27 + continuation: fiber,
28 + props: yieldNode.props,
29 + };
30 +};
31 +
32 +exports.createUpdatedReifiedYield = function(previousYield : ReifiedYield, yieldNode : ReactYield) : ReifiedYield {
33 + return {
34 + continuation: previousYield.continuation,
35 + props: yieldNode.props,
36 + };
37 +};
src/renderers/shared/fiber/ReactStateNode.js deleted
-23
@@ -1,23 +0,0 @@
1 -/**
2 - * Copyright 2013-present, Facebook, Inc.
3 - * All rights reserved.
4 - *
5 - * This source code is licensed under the BSD-style license found in the
6 - * LICENSE file in the root directory of this source tree. An additional grant
7 - * of patent rights can be found in the PATENTS file in the same directory.
8 - *
9 - * @providesModule ReactStateNode
10 - * @flow
11 - */
12 -
13 -'use strict';
14 -
15 -type StateNode = {
16 - next: ?{ [key: string]: StateNode },
17 -};
18 -
19 -module.exports = function() : StateNode {
20 - return {
21 - next: null,
22 - };
23 -};
src/renderers/shared/fiber/isomorphic/ReactCoroutine.js new
+109
@@ -0,0 +1,109 @@
1 +/**
2 + * Copyright 2014-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule ReactCoroutine
10 + * @flow
11 + */
12 +
13 +'use strict';
14 +
15 +import type { ReactNodeList } from 'ReactTypes';
16 +
17 +// The Symbol used to tag the special React types. If there is no native Symbol
18 +// nor polyfill, then a plain number is used for performance.
19 +var REACT_COROUTINE_TYPE =
20 + (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.coroutine')) ||
21 + 0xeac8;
22 +
23 +var REACT_YIELD_TYPE =
24 + (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.yield')) ||
25 + 0xeac9;
26 +
27 +type ReifiedYield = { continuation: Object, props: Object };
28 +type CoroutineHandler<T> = (props: T, yields: Array<ReifiedYield>) => ReactNodeList;
29 +
30 +export type ReactCoroutine = {
31 + $$typeof: Symbol | number,
32 + key: ?string,
33 + children: any,
34 + // This should be a more specific CoroutineHandler
35 + handler: (props: any, yields: Array<ReifiedYield>) => ReactNodeList,
36 + props: mixed,
37 +};
38 +export type ReactYield = {
39 + $$typeof: Symbol | number,
40 + key: ?string,
41 + props: Object,
42 + continuation: mixed
43 +};
44 +
45 +exports.createCoroutine = function<T>(children : mixed, handler : CoroutineHandler<T>, props : T, key : ?string = null) : ReactCoroutine {
46 + var coroutine = {
47 + // This tag allow us to uniquely identify this as a React Coroutine
48 + $$typeof: REACT_COROUTINE_TYPE,
49 + key: key == null ? null : '' + key,
50 + children: children,
51 + handler: handler,
52 + props: props,
53 + };
54 +
55 + if (__DEV__) {
56 + // TODO: Add _store property for marking this as validated.
57 + if (Object.freeze) {
58 + Object.freeze(coroutine.props);
59 + Object.freeze(coroutine);
60 + }
61 + }
62 +
63 + return coroutine;
64 +};
65 +
66 +exports.createYield = function(props : mixed, continuation : mixed, key : ?string = null) {
67 + var yieldNode = {
68 + // This tag allow us to uniquely identify this as a React Yield
69 + $$typeof: REACT_YIELD_TYPE,
70 + key: key == null ? null : '' + key,
71 + props: props,
72 + continuation: continuation,
73 + };
74 +
75 + if (__DEV__) {
76 + // TODO: Add _store property for marking this as validated.
77 + if (Object.freeze) {
78 + Object.freeze(yieldNode.props);
79 + Object.freeze(yieldNode);
80 + }
81 + }
82 +
83 + return yieldNode;
84 +};
85 +
86 +/**
87 + * Verifies the object is a coroutine object.
88 + */
89 +exports.isCoroutine = function(object : mixed) : boolean {
90 + return (
91 + typeof object === 'object' &&
92 + object !== null &&
93 + object.$$typeof === REACT_COROUTINE_TYPE
94 + );
95 +};
96 +
97 +/**
98 + * Verifies the object is a yield object.
99 + */
100 +exports.isYield = function(object : mixed) : boolean {
101 + return (
102 + typeof object === 'object' &&
103 + object !== null &&
104 + object.$$typeof === REACT_YIELD_TYPE
105 + );
106 +};
107 +
108 +exports.REACT_YIELD_TYPE = REACT_YIELD_TYPE;
109 +exports.REACT_COROUTINE_TYPE = REACT_COROUTINE_TYPE;
src/renderers/shared/fiber/isomorphic/ReactTypes.js new
+25
@@ -0,0 +1,25 @@
1 +/**
2 + * Copyright 2014-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule ReactTypes
10 + * @flow
11 + */
12 +
13 +'use strict';
14 +
15 +import type { ReactCoroutine, ReactYield } from 'ReactCoroutine';
16 +
17 +export type ReactNode = ReactElement | ReactCoroutine | ReactYield | ReactText | ReactFragment;
18 +
19 +export type ReactFragment = ReactEmpty | Iterable<ReactNode>;
20 +
21 +export type ReactNodeList = ReactEmpty | ReactNode;
22 +
23 +export type ReactText = string | number;
24 +
25 +export type ReactEmpty = null | void | boolean;
src/renderers/shared/stack/reconciler/__tests__/ReactComponent-test.js
+1 -7
@@ -280,14 +280,8 @@ describe('ReactComponent', function() {
280 'or a class/function (for composite components) but got: null.'
281 );
282
283 - var Z = {};
284 - expect(() => ReactTestUtils.renderIntoDocument(<Z />)).toThrowError(
285 - 'Element type is invalid: expected a string (for built-in components) ' +
286 - 'or a class/function (for composite components) but got: object.'
287 - );
288 -
283 // One warning for each element creation
290 - expect(console.error.calls.count()).toBe(3);
284 + expect(console.error.calls.count()).toBe(2);
285 });
286
287 });