Merge pull request #6859 from sebmarkbage/newreconciler

[Fiber] Child Reconciler + New Coroutines Primitive
This commit is contained in:
Sebastian Markbåge
2016-05-26 18:21:08 -07:00
15 changed files with 714 additions and 109 deletions
@@ -398,4 +398,6 @@ ReactElement.isValidElement = function(object) {
);
};
ReactElement.REACT_ELEMENT_TYPE = REACT_ELEMENT_TYPE;
module.exports = ReactElement;
@@ -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(
+50 -2
View File
@@ -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 <div>Hello World</div>;
return <span><div>Hello World</div></span>;
}
function Foo() {
return <Bar isBar={true} />;
return [
<Bar isBar={true} />,
<Bar isBar={true} />,
];
}
ReactNoop.render(<Foo />);
@@ -53,5 +59,47 @@ describe('ReactComponent', function() {
// console.log('Done');
});
it('should render a coroutine', function() {
function Continuation({ isSame }) {
return <span>{isSame ? 'foo==bar' : 'foo!=bar'}</span>;
}
// 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 [<Child bar={true} />, <Child bar={false} />];
}
function HandleYields(props, yields) {
return yields.map(y =>
<y.continuation isSame={props.foo === y.props.bar} />
);
}
// 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 <div><Parent foo={true}><Indirection /></Parent></div>;
}
ReactNoop.render(<App />);
ReactNoop.flush();
});
});
@@ -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);
};
+70 -13
View File
@@ -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;
};
@@ -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;
@@ -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<ReifiedYield> = [];
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;
};
@@ -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;
};
@@ -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<T, P> = {
type: T,
@@ -54,16 +49,34 @@ module.exports = function<T, P, I>(config : HostConfig<T, P, I>) : 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<T, P, I>(config : HostConfig<T, P, I>) : Reconciler {
ensureLowPriIsScheduled();
nextUnitOfWork = ReactFiberFunctionalComponent.createFiber(element);
nextUnitOfWork = ReactFiber.createFiberFromElement(element);
return {};
},
@@ -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,
};
};
@@ -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,
};
};
@@ -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;
@@ -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<T> = (props: T, yields: Array<ReifiedYield>) => ReactNodeList;
export type ReactCoroutine = {
$$typeof: Symbol | number,
key: ?string,
children: any,
// This should be a more specific CoroutineHandler
handler: (props: any, yields: Array<ReifiedYield>) => ReactNodeList,
props: mixed,
};
export type ReactYield = {
$$typeof: Symbol | number,
key: ?string,
props: Object,
continuation: mixed
};
exports.createCoroutine = function<T>(children : mixed, handler : CoroutineHandler<T>, 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;
@@ -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<ReactNode>;
export type ReactNodeList = ReactEmpty | ReactNode;
export type ReactText = string | number;
export type ReactEmpty = null | void | boolean;
@@ -280,14 +280,8 @@ describe('ReactComponent', function() {
'or a class/function (for composite components) but got: null.'
);
var Z = {};
expect(() => ReactTestUtils.renderIntoDocument(<Z />)).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);
});
});