@samitouri / QOS-React / commits / b77b12311f

Call and Return components should use ReactElement (#11834)

* Call and Return components should use ReactElement ReactChildFiber contains lots of branches that do the same thing for different child types. We can unify them by having more child types be ReactElements. This requires that the `type` and `key` fields are sufficient to determine the identity of the child. The main benefit is decreased file size, especially as we add more component types, like context providers and consumers. This updates Call and Return components to use ReactElement. Portals are left alone for now because their identity includes the host instance. * Move server render invariant for call and return types * Sort ReactElement type checks by most likely * Performance timeline should skip over call components Don't think these were intentionally omitted from the blacklist of component types. I went ahead and updated getComponentName to include special types, even though I don't think they're used anywhere right now. * Remove surrounding brackets from internal display names

Andrew Clark committed Dec 12, 2017 at 15:04 UTC b77b12311f0c66aad9b50f805c53dcc05d2ea75c
12 files changed +195 -366
packages/react-call-return/src/ReactCallReturn.js
+29 -16
@@ -7,25 +7,33 @@
7 * @flow
8 */
9
10 -import {REACT_CALL_TYPE, REACT_RETURN_TYPE} from 'shared/ReactSymbols';
10 +import {
11 + REACT_CALL_TYPE,
12 + REACT_RETURN_TYPE,
13 + REACT_ELEMENT_TYPE,
14 +} from 'shared/ReactSymbols';
15
16 import type {ReactCall, ReactNodeList, ReactReturn} from 'shared/ReactTypes';
17
14 -type CallHandler<T> = (props: T, returns: Array<mixed>) => ReactNodeList;
18 +type CallHandler<T, V> = (props: T, returns: Array<V>) => ReactNodeList;
19
16 -export function unstable_createCall<T>(
17 - children: mixed,
18 - handler: CallHandler<T>,
20 +export function unstable_createCall<T, V>(
21 + children: ReactNodeList,
22 + handler: CallHandler<T, V>,
23 props: T,
24 key: ?string = null,
21 -): ReactCall {
25 +): ReactCall<V> {
26 const call = {
27 // This tag allow us to uniquely identify this as a React Call
24 - $$typeof: REACT_CALL_TYPE,
28 + $$typeof: REACT_ELEMENT_TYPE,
29 + type: REACT_CALL_TYPE,
30 key: key == null ? null : '' + key,
26 - children: children,
27 - handler: handler,
28 - props: props,
31 + ref: null,
32 + props: {
33 + props,
34 + handler,
35 + children: children,
36 + },
37 };
38
39 if (__DEV__) {
@@ -39,11 +47,16 @@ export function unstable_createCall<T>(
47 return call;
48 }
49
42 -export function unstable_createReturn(value: mixed): ReactReturn {
50 +export function unstable_createReturn<V>(value: V): ReactReturn<V> {
51 const returnNode = {
44 - // This tag allow us to uniquely identify this as a React Return
45 - $$typeof: REACT_RETURN_TYPE,
46 - value: value,
52 + // This tag allow us to uniquely identify this as a React Call
53 + $$typeof: REACT_ELEMENT_TYPE,
54 + type: REACT_RETURN_TYPE,
55 + key: null,
56 + ref: null,
57 + props: {
58 + value,
59 + },
60 };
61
62 if (__DEV__) {
@@ -63,7 +76,7 @@ export function unstable_isCall(object: mixed): boolean {
76 return (
77 typeof object === 'object' &&
78 object !== null &&
66 - object.$$typeof === REACT_CALL_TYPE
79 + object.type === REACT_CALL_TYPE
80 );
81 }
82
@@ -74,7 +87,7 @@ export function unstable_isReturn(object: mixed): boolean {
87 return (
88 typeof object === 'object' &&
89 object !== null &&
77 - object.$$typeof === REACT_RETURN_TYPE
90 + object.type === REACT_RETURN_TYPE
91 );
92 }
93
packages/react-dom/src/server/ReactPartialRenderer.js
+31 -27
@@ -621,11 +621,6 @@ class ReactDOMServerRenderer {
621 'Portals are not currently supported by the server renderer. ' +
622 'Render them conditionally so that they only appear on the client render.',
623 );
624 - invariant(
625 - $$typeof !== REACT_CALL_TYPE && $$typeof !== REACT_RETURN_TYPE,
626 - 'The experimental Call and Return types are not currently ' +
627 - 'supported by the server renderer.',
628 - );
624 // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type.
625 invariant(
626 false,
@@ -647,28 +642,37 @@ class ReactDOMServerRenderer {
642 }
643 this.stack.push(frame);
644 return '';
650 - } else if (
651 - ((nextChild: any): ReactElement).type === REACT_FRAGMENT_TYPE
652 - ) {
653 - const nextChildren = toArray(
654 - ((nextChild: any): ReactElement).props.children,
655 - );
656 - const frame: Frame = {
657 - domNamespace: parentNamespace,
658 - children: nextChildren,
659 - childIndex: 0,
660 - context: context,
661 - footer: '',
662 - };
663 - if (__DEV__) {
664 - ((frame: any): FrameDev).debugElementStack = [];
665 - }
666 - this.stack.push(frame);
667 - return '';
668 - } else {
669 - // Safe because we just checked it's an element.
670 - const nextElement = ((nextChild: any): ReactElement);
671 - return this.renderDOM(nextElement, context, parentNamespace);
645 + }
646 + // Safe because we just checked it's an element.
647 + const nextElement = ((nextChild: any): ReactElement);
648 + const elementType = nextElement.type;
649 + switch (elementType) {
650 + case REACT_FRAGMENT_TYPE:
651 + const nextChildren = toArray(
652 + ((nextChild: any): ReactElement).props.children,
653 + );
654 + const frame: Frame = {
655 + domNamespace: parentNamespace,
656 + children: nextChildren,
657 + childIndex: 0,
658 + context: context,
659 + footer: '',
660 + };
661 + if (__DEV__) {
662 + ((frame: any): FrameDev).debugElementStack = [];
663 + }
664 + this.stack.push(frame);
665 + return '';
666 + case REACT_CALL_TYPE:
667 + case REACT_RETURN_TYPE:
668 + invariant(
669 + false,
670 + 'The experimental Call and Return types are not currently ' +
671 + 'supported by the server renderer.',
672 + );
673 + // eslint-disable-next-line-no-fallthrough
674 + default:
675 + return this.renderDOM(nextElement, context, parentNamespace);
676 }
677 }
678 }
packages/react-reconciler/src/ReactChildFiber.js
+3 -228
@@ -8,7 +8,7 @@
8 */
9
10 import type {ReactElement} from 'shared/ReactElementType';
11 -import type {ReactCall, ReactPortal, ReactReturn} from 'shared/ReactTypes';
11 +import type {ReactPortal} from 'shared/ReactTypes';
12 import type {Fiber} from 'react-reconciler/src/ReactFiber';
13 import type {ExpirationTime} from 'react-reconciler/src/ReactFiberExpirationTime';
14
@@ -17,8 +17,6 @@ import {
17 getIteratorFn,
18 REACT_ELEMENT_TYPE,
19 REACT_FRAGMENT_TYPE,
20 - REACT_CALL_TYPE,
21 - REACT_RETURN_TYPE,
20 REACT_PORTAL_TYPE,
21 } from 'shared/ReactSymbols';
22 import {
@@ -26,8 +24,6 @@ import {
24 ClassComponent,
25 HostText,
26 HostPortal,
29 - CallComponent,
30 - ReturnComponent,
27 Fragment,
28 } from 'shared/ReactTypeOfWork';
29 import emptyObject from 'fbjs/lib/emptyObject';
@@ -39,8 +35,6 @@ import {
35 createFiberFromElement,
36 createFiberFromFragment,
37 createFiberFromText,
42 - createFiberFromCall,
43 - createFiberFromReturn,
38 createFiberFromPortal,
39 } from './ReactFiber';
40 import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
@@ -363,55 +357,6 @@ function ChildReconciler(shouldTrackSideEffects) {
357 }
358 }
359
366 - function updateCall(
367 - returnFiber: Fiber,
368 - current: Fiber | null,
369 - call: ReactCall,
370 - expirationTime: ExpirationTime,
371 - ): Fiber {
372 - // TODO: Should this also compare handler to determine whether to reuse?
373 - if (current === null || current.tag !== CallComponent) {
374 - // Insert
375 - const created = createFiberFromCall(
376 - call,
377 - returnFiber.internalContextTag,
378 - expirationTime,
379 - );
380 - created.return = returnFiber;
381 - return created;
382 - } else {
383 - // Move based on index
384 - const existing = useFiber(current, call, expirationTime);
385 - existing.return = returnFiber;
386 - return existing;
387 - }
388 - }
389 -
390 - function updateReturn(
391 - returnFiber: Fiber,
392 - current: Fiber | null,
393 - returnNode: ReactReturn,
394 - expirationTime: ExpirationTime,
395 - ): Fiber {
396 - if (current === null || current.tag !== ReturnComponent) {
397 - // Insert
398 - const created = createFiberFromReturn(
399 - returnNode,
400 - returnFiber.internalContextTag,
401 - expirationTime,
402 - );
403 - created.type = returnNode.value;
404 - created.return = returnFiber;
405 - return created;
406 - } else {
407 - // Move based on index
408 - const existing = useFiber(current, null, expirationTime);
409 - existing.type = returnNode.value;
410 - existing.return = returnFiber;
411 - return existing;
412 - }
413 - }
414 -
360 function updatePortal(
361 returnFiber: Fiber,
362 current: Fiber | null,
@@ -486,48 +431,15 @@ function ChildReconciler(shouldTrackSideEffects) {
431 if (typeof newChild === 'object' && newChild !== null) {
432 switch (newChild.$$typeof) {
433 case REACT_ELEMENT_TYPE: {
489 - if (newChild.type === REACT_FRAGMENT_TYPE) {
490 - const created = createFiberFromFragment(
491 - newChild.props.children,
492 - returnFiber.internalContextTag,
493 - expirationTime,
494 - newChild.key,
495 - );
496 - created.return = returnFiber;
497 - return created;
498 - } else {
499 - const created = createFiberFromElement(
500 - newChild,
501 - returnFiber.internalContextTag,
502 - expirationTime,
503 - );
504 - created.ref = coerceRef(null, newChild);
505 - created.return = returnFiber;
506 - return created;
507 - }
508 - }
509 -
510 - case REACT_CALL_TYPE: {
511 - const created = createFiberFromCall(
434 + const created = createFiberFromElement(
435 newChild,
436 returnFiber.internalContextTag,
437 expirationTime,
438 );
439 + created.ref = coerceRef(null, newChild);
440 created.return = returnFiber;
441 return created;
442 }
519 -
520 - case REACT_RETURN_TYPE: {
521 - const created = createFiberFromReturn(
522 - newChild,
523 - returnFiber.internalContextTag,
524 - expirationTime,
525 - );
526 - created.type = newChild.value;
527 - created.return = returnFiber;
528 - return created;
529 - }
530 -
443 case REACT_PORTAL_TYPE: {
444 const created = createFiberFromPortal(
445 newChild,
@@ -610,31 +522,6 @@ function ChildReconciler(shouldTrackSideEffects) {
522 return null;
523 }
524 }
613 -
614 - case REACT_CALL_TYPE: {
615 - if (newChild.key === key) {
616 - return updateCall(returnFiber, oldFiber, newChild, expirationTime);
617 - } else {
618 - return null;
619 - }
620 - }
621 -
622 - case REACT_RETURN_TYPE: {
623 - // Returns don't have keys. If the previous node is implicitly keyed
624 - // we can continue to replace it without aborting even if it is not a
625 - // yield.
626 - if (key === null) {
627 - return updateReturn(
628 - returnFiber,
629 - oldFiber,
630 - newChild,
631 - expirationTime,
632 - );
633 - } else {
634 - return null;
635 - }
636 - }
637 -
525 case REACT_PORTAL_TYPE: {
526 if (newChild.key === key) {
527 return updatePortal(
@@ -717,32 +604,6 @@ function ChildReconciler(shouldTrackSideEffects) {
604 expirationTime,
605 );
606 }
720 -
721 - case REACT_CALL_TYPE: {
722 - const matchedFiber =
723 - existingChildren.get(
724 - newChild.key === null ? newIdx : newChild.key,
725 - ) || null;
726 - return updateCall(
727 - returnFiber,
728 - matchedFiber,
729 - newChild,
730 - expirationTime,
731 - );
732 - }
733 -
734 - case REACT_RETURN_TYPE: {
735 - // Returns don't have keys, so we neither have to check the old nor
736 - // new node for the key. If both are returns, they match.
737 - const matchedFiber = existingChildren.get(newIdx) || null;
738 - return updateReturn(
739 - returnFiber,
740 - matchedFiber,
741 - newChild,
742 - expirationTime,
743 - );
744 - }
745 -
607 case REACT_PORTAL_TYPE: {
608 const matchedFiber =
609 existingChildren.get(
@@ -793,7 +654,6 @@ function ChildReconciler(shouldTrackSideEffects) {
654 }
655 switch (child.$$typeof) {
656 case REACT_ELEMENT_TYPE:
796 - case REACT_CALL_TYPE:
657 case REACT_PORTAL_TYPE:
658 warnForMissingKey(child);
659 const key = child.key;
@@ -1252,72 +1112,6 @@ function ChildReconciler(shouldTrackSideEffects) {
1112 }
1113 }
1114
1255 - function reconcileSingleCall(
1256 - returnFiber: Fiber,
1257 - currentFirstChild: Fiber | null,
1258 - call: ReactCall,
1259 - expirationTime: ExpirationTime,
1260 - ): Fiber {
1261 - const key = call.key;
1262 - let child = currentFirstChild;
1263 - while (child !== null) {
1264 - // TODO: If key === null and child.key === null, then this only applies to
1265 - // the first item in the list.
1266 - if (child.key === key) {
1267 - if (child.tag === CallComponent) {
1268 - deleteRemainingChildren(returnFiber, child.sibling);
1269 - const existing = useFiber(child, call, expirationTime);
1270 - existing.return = returnFiber;
1271 - return existing;
1272 - } else {
1273 - deleteRemainingChildren(returnFiber, child);
1274 - break;
1275 - }
1276 - } else {
1277 - deleteChild(returnFiber, child);
1278 - }
1279 - child = child.sibling;
1280 - }
1281 -
1282 - const created = createFiberFromCall(
1283 - call,
1284 - returnFiber.internalContextTag,
1285 - expirationTime,
1286 - );
1287 - created.return = returnFiber;
1288 - return created;
1289 - }
1290 -
1291 - function reconcileSingleReturn(
1292 - returnFiber: Fiber,
1293 - currentFirstChild: Fiber | null,
1294 - returnNode: ReactReturn,
1295 - expirationTime: ExpirationTime,
1296 - ): Fiber {
1297 - // There's no need to check for keys on yields since they're stateless.
1298 - let child = currentFirstChild;
1299 - if (child !== null) {
1300 - if (child.tag === ReturnComponent) {
1301 - deleteRemainingChildren(returnFiber, child.sibling);
1302 - const existing = useFiber(child, null, expirationTime);
1303 - existing.type = returnNode.value;
1304 - existing.return = returnFiber;
1305 - return existing;
1306 - } else {
1307 - deleteRemainingChildren(returnFiber, child);
1308 - }
1309 - }
1310 -
1311 - const created = createFiberFromReturn(
1312 - returnNode,
1313 - returnFiber.internalContextTag,
1314 - expirationTime,
1315 - );
1316 - created.type = returnNode.value;
1317 - created.return = returnFiber;
1318 - return created;
1319 - }
1320 -
1115 function reconcileSinglePortal(
1116 returnFiber: Fiber,
1117 currentFirstChild: Fiber | null,
@@ -1402,25 +1196,6 @@ function ChildReconciler(shouldTrackSideEffects) {
1196 expirationTime,
1197 ),
1198 );
1405 -
1406 - case REACT_CALL_TYPE:
1407 - return placeSingleChild(
1408 - reconcileSingleCall(
1409 - returnFiber,
1410 - currentFirstChild,
1411 - newChild,
1412 - expirationTime,
1413 - ),
1414 - );
1415 - case REACT_RETURN_TYPE:
1416 - return placeSingleChild(
1417 - reconcileSingleReturn(
1418 - returnFiber,
1419 - currentFirstChild,
1420 - newChild,
1421 - expirationTime,
1422 - ),
1423 - );
1199 case REACT_PORTAL_TYPE:
1200 return placeSingleChild(
1201 reconcileSinglePortal(
packages/react-reconciler/src/ReactDebugFiberPerf.js
+2
@@ -16,6 +16,7 @@ import {
16 HostComponent,
17 HostText,
18 HostPortal,
19 + CallComponent,
20 ReturnComponent,
21 Fragment,
22 } from 'shared/ReactTypeOfWork';
@@ -166,6 +167,7 @@ const shouldIgnoreFiber = (fiber: Fiber): boolean => {
167 case HostComponent:
168 case HostText:
169 case HostPortal:
170 + case CallComponent:
171 case ReturnComponent:
172 case Fragment:
173 return true;
packages/react-reconciler/src/ReactFiber.js
+75 -61
@@ -7,12 +7,7 @@
7 */
8
9 import type {ReactElement, Source} from 'shared/ReactElementType';
10 -import type {
11 - ReactCall,
12 - ReactFragment,
13 - ReactPortal,
14 - ReactReturn,
15 -} from 'shared/ReactTypes';
10 +import type {ReactFragment, ReactPortal} from 'shared/ReactTypes';
11 import type {TypeOfWork} from 'shared/ReactTypeOfWork';
12 import type {TypeOfInternalContext} from './ReactTypeOfInternalContext';
13 import type {TypeOfSideEffect} from 'shared/ReactTypeOfSideEffect';
@@ -36,6 +31,11 @@ import getComponentName from 'shared/getComponentName';
31
32 import {NoWork} from './ReactFiberExpirationTime';
33 import {NoContext, AsyncUpdates} from './ReactTypeOfInternalContext';
34 +import {
35 + REACT_FRAGMENT_TYPE,
36 + REACT_RETURN_TYPE,
37 + REACT_CALL_TYPE,
38 +} from 'shared/ReactSymbols';
39
40 let hasBadMapPolyfill;
41
@@ -324,44 +324,79 @@ export function createFiberFromElement(
324 } else if (typeof type === 'string') {
325 fiber = createFiber(HostComponent, pendingProps, key, internalContextTag);
326 fiber.type = type;
327 - } else if (
328 - typeof type === 'object' &&
329 - type !== null &&
330 - typeof type.tag === 'number'
331 - ) {
332 - // Currently assumed to be a continuation and therefore is a fiber already.
333 - // TODO: The yield system is currently broken for updates in some cases.
334 - // The reified yield stores a fiber, but we don't know which fiber that is;
335 - // the current or a workInProgress? When the continuation gets rendered here
336 - // we don't know if we can reuse that fiber or if we need to clone it.
337 - // There is probably a clever way to restructure this.
338 - fiber = ((type: any): Fiber);
339 - fiber.pendingProps = pendingProps;
327 } else {
341 - let info = '';
342 - if (__DEV__) {
343 - if (
344 - type === undefined ||
345 - (typeof type === 'object' &&
328 + switch (type) {
329 + case REACT_FRAGMENT_TYPE:
330 + return createFiberFromFragment(
331 + pendingProps.children,
332 + internalContextTag,
333 + expirationTime,
334 + key,
335 + );
336 + case REACT_CALL_TYPE:
337 + fiber = createFiber(
338 + CallComponent,
339 + pendingProps,
340 + key,
341 + internalContextTag,
342 + );
343 + fiber.type = REACT_CALL_TYPE;
344 + break;
345 + case REACT_RETURN_TYPE:
346 + fiber = createFiber(
347 + ReturnComponent,
348 + pendingProps,
349 + key,
350 + internalContextTag,
351 + );
352 + fiber.type = REACT_RETURN_TYPE;
353 + break;
354 + default: {
355 + if (
356 + typeof type === 'object' &&
357 type !== null &&
347 - Object.keys(type).length === 0)
348 - ) {
349 - info +=
350 - ' You likely forgot to export your component from the file ' +
351 - "it's defined in, or you might have mixed up default and named imports.";
352 - }
353 - const ownerName = owner ? getComponentName(owner) : null;
354 - if (ownerName) {
355 - info += '\n\nCheck the render method of `' + ownerName + '`.';
358 + typeof type.tag === 'number'
359 + ) {
360 + // Currently assumed to be a continuation and therefore is a
361 + // fiber already.
362 + // TODO: The yield system is currently broken for updates in some
363 + // cases. The reified yield stores a fiber, but we don't know which
364 + // fiber that is; the current or a workInProgress? When the
365 + // continuation gets rendered here we don't know if we can reuse that
366 + // fiber or if we need to clone it. There is probably a clever way to
367 + // restructure this.
368 + fiber = ((type: any): Fiber);
369 + fiber.pendingProps = pendingProps;
370 + } else {
371 + let info = '';
372 + if (__DEV__) {
373 + if (
374 + type === undefined ||
375 + (typeof type === 'object' &&
376 + type !== null &&
377 + Object.keys(type).length === 0)
378 + ) {
379 + info +=
380 + ' You likely forgot to export your component from the file ' +
381 + "it's defined in, or you might have mixed up default and " +
382 + 'named imports.';
383 + }
384 + const ownerName = owner ? getComponentName(owner) : null;
385 + if (ownerName) {
386 + info += '\n\nCheck the render method of `' + ownerName + '`.';
387 + }
388 + }
389 + invariant(
390 + false,
391 + 'Element type is invalid: expected a string (for built-in ' +
392 + 'components) or a class/function (for composite components) ' +
393 + 'but got: %s.%s',
394 + type == null ? type : typeof type,
395 + info,
396 + );
397 + }
398 }
399 }
358 - invariant(
359 - false,
360 - 'Element type is invalid: expected a string (for built-in components) ' +
361 - 'or a class/function (for composite components) but got: %s.%s',
362 - type == null ? type : typeof type,
363 - info,
364 - );
400 }
401
402 if (__DEV__) {
@@ -401,27 +436,6 @@ export function createFiberFromHostInstanceForDeletion(): Fiber {
436 return fiber;
437 }
438
404 -export function createFiberFromCall(
405 - call: ReactCall,
406 - internalContextTag: TypeOfInternalContext,
407 - expirationTime: ExpirationTime,
408 -): Fiber {
409 - const fiber = createFiber(CallComponent, call, call.key, internalContextTag);
410 - fiber.type = call.handler;
411 - fiber.expirationTime = expirationTime;
412 - return fiber;
413 -}
414 -
415 -export function createFiberFromReturn(
416 - returnNode: ReactReturn,
417 - internalContextTag: TypeOfInternalContext,
418 - expirationTime: ExpirationTime,
419 -): Fiber {
420 - const fiber = createFiber(ReturnComponent, null, null, internalContextTag);
421 - fiber.expirationTime = expirationTime;
422 - return fiber;
423 -}
424 -
439 export function createFiberFromPortal(
440 portal: ReactPortal,
441 internalContextTag: TypeOfInternalContext,
packages/react-reconciler/src/ReactFiberBeginWork.js
+5 -6
@@ -8,7 +8,6 @@
8 */
9
10 import type {HostConfig} from 'react-reconciler';
11 -import type {ReactCall} from 'shared/ReactTypes';
11 import type {Fiber} from 'react-reconciler/src/ReactFiber';
12 import type {HostContext} from './ReactFiberHostContext';
13 import type {HydrationContext} from './ReactFiberHydrationContext';
@@ -535,18 +534,18 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
534 }
535
536 function updateCallComponent(current, workInProgress, renderExpirationTime) {
538 - let nextCall = (workInProgress.pendingProps: ReactCall);
537 + let nextProps = workInProgress.pendingProps;
538 if (hasContextChanged()) {
539 // Normally we can bail out on props equality but if context has changed
540 // we don't do the bailout and we have to reuse existing props instead.
542 - } else if (workInProgress.memoizedProps === nextCall) {
543 - nextCall = workInProgress.memoizedProps;
541 + } else if (workInProgress.memoizedProps === nextProps) {
542 + nextProps = workInProgress.memoizedProps;
543 // TODO: When bailing out, we might need to return the stateNode instead
544 // of the child. To check it for work.
545 // return bailoutOnAlreadyFinishedWork(current, workInProgress);
546 }
547
549 - const nextChildren = nextCall.children;
548 + const nextChildren = nextProps.children;
549
550 // The following is a fork of reconcileChildrenAtExpirationTime but using
551 // stateNode to store the child.
@@ -566,7 +565,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
565 );
566 }
567
569 - memoizeProps(workInProgress, nextCall);
568 + memoizeProps(workInProgress, nextProps);
569 // This doesn't take arbitrary time so we could synchronously just begin
570 // eagerly do the work of workInProgress.child as an optimization.
571 return workInProgress.stateNode;
packages/react-reconciler/src/ReactFiberCompleteWork.js
+6 -7
@@ -8,7 +8,6 @@
8 */
9
10 import type {HostConfig} from 'react-reconciler';
11 -import type {ReactCall} from 'shared/ReactTypes';
11 import type {Fiber} from './ReactFiber';
12 import type {ExpirationTime} from './ReactFiberExpirationTime';
13 import type {HostContext} from './ReactFiberHostContext';
@@ -93,7 +92,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
92 ) {
93 invariant(false, 'A call cannot have host component children.');
94 } else if (node.tag === ReturnComponent) {
96 - returns.push(node.type);
95 + returns.push(node.pendingProps.value);
96 } else if (node.child !== null) {
97 node.child.return = node;
98 node = node.child;
@@ -115,9 +114,9 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
114 workInProgress: Fiber,
115 renderExpirationTime: ExpirationTime,
116 ) {
118 - const call = (workInProgress.memoizedProps: ?ReactCall);
117 + const props = workInProgress.memoizedProps;
118 invariant(
120 - call,
119 + props,
120 'Should be resolved by now. This error is likely caused by a bug in ' +
121 'React. Please file an issue.',
122 );
@@ -135,9 +134,9 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
134 // TODO: Compare this to a generator or opaque helpers like Children.
135 const returns: Array<mixed> = [];
136 appendAllReturns(returns, workInProgress);
138 - const fn = call.handler;
139 - const props = call.props;
140 - const nextChildren = fn(props, returns);
137 + const fn = props.handler;
138 + const childProps = props.props;
139 + const nextChildren = fn(childProps, returns);
140
141 const currentFirstChild = current !== null ? current.child : null;
142 workInProgress.child = reconcileChildFibers(
packages/react-reconciler/src/__tests__/__snapshots__/ReactIncrementalPerf-test.internal.js.snap
+3 -4
@@ -298,10 +298,9 @@ exports[`ReactDebugFiberPerf supports returns 1`] = `
298 ⚛ (React Tree Reconciliation)
299 ⚛ App [mount]
300 ⚛ CoParent [mount]
301 - ⚛ HandleReturns [mount]
302 - ⚛ Indirection [mount]
303 - ⚛ CoChild [mount]
304 - ⚛ CoChild [mount]
301 + ⚛ Indirection [mount]
302 + ⚛ CoChild [mount]
303 + ⚛ CoChild [mount]
304 ⚛ Continuation [mount]
305 ⚛ Continuation [mount]
306
packages/react/src/ReactChildren.js
-4
@@ -11,8 +11,6 @@ import warning from 'fbjs/lib/warning';
11 import {
12 getIteratorFn,
13 REACT_ELEMENT_TYPE,
14 - REACT_CALL_TYPE,
15 - REACT_RETURN_TYPE,
14 REACT_PORTAL_TYPE,
15 } from 'shared/ReactSymbols';
16
@@ -125,8 +123,6 @@ function traverseAllChildrenImpl(
123 case 'object':
124 switch (children.$$typeof) {
125 case REACT_ELEMENT_TYPE:
128 - case REACT_CALL_TYPE:
129 - case REACT_RETURN_TYPE:
126 case REACT_PORTAL_TYPE:
127 invokeCallback = true;
128 }
packages/react/src/__tests__/ReactChildren-test.js
+4 -2
@@ -95,7 +95,8 @@ describe('ReactChildren', () => {
95 context,
96 );
97 expect(callback).toHaveBeenCalledWith(reactCall, 0);
98 - expect(mappedChildren[0]).toEqual(reactCall);
98 + expect(mappedChildren[0].type).toEqual(reactCall.type);
99 + expect(mappedChildren[0].props).toEqual(reactCall.props);
100 });
101
102 it('should support Return components', () => {
@@ -119,7 +120,8 @@ describe('ReactChildren', () => {
120 context,
121 );
122 expect(callback).toHaveBeenCalledWith(reactReturn, 0);
122 - expect(mappedChildren[0]).toEqual(reactReturn);
123 + expect(mappedChildren[0].props).toEqual(reactReturn.props);
124 + expect(mappedChildren[0].type).toEqual(reactReturn.type);
125 });
126
127 it('should treat single arrayless child as being in array', () => {
packages/shared/ReactTypes.js
+18 -9
@@ -10,8 +10,8 @@
10
11 export type ReactNode =
12 | React$Element<any>
13 - | ReactCall
14 - | ReactReturn
13 + | ReactCall<any>
14 + | ReactReturn<any>
15 | ReactPortal
16 | ReactText
17 | ReactFragment;
@@ -24,18 +24,27 @@ export type ReactText = string | number;
24
25 export type ReactEmpty = null | void | boolean;
26
27 -export type ReactCall = {
27 +export type ReactCall<V> = {
28 $$typeof: Symbol | number,
29 + type: Symbol | number,
30 key: null | string,
30 - children: any,
31 - // This should be a more specific CallHandler
32 - handler: (props: any, returns: Array<mixed>) => ReactNodeList,
33 - props: any,
31 + ref: null,
32 + props: {
33 + props: any,
34 + // This should be a more specific CallHandler
35 + handler: (props: any, returns: Array<V>) => ReactNodeList,
36 + children?: ReactNodeList,
37 + },
38 };
39
36 -export type ReactReturn = {
40 +export type ReactReturn<V> = {
41 $$typeof: Symbol | number,
38 - value: mixed,
42 + type: Symbol | number,
43 + key: null,
44 + ref: null,
45 + props: {
46 + value: V,
47 + },
48 };
49
50 export type ReactPortal = {
packages/shared/getComponentName.js
+19 -2
@@ -9,13 +9,30 @@
9
10 import type {Fiber} from 'react-reconciler/src/ReactFiber';
11
12 +import {
13 + REACT_CALL_TYPE,
14 + REACT_FRAGMENT_TYPE,
15 + REACT_RETURN_TYPE,
16 + REACT_PORTAL_TYPE,
17 +} from 'shared/ReactSymbols';
18 +
19 function getComponentName(fiber: Fiber): string | null {
20 const {type} = fiber;
21 + if (typeof type === 'function') {
22 + return type.displayName || type.name;
23 + }
24 if (typeof type === 'string') {
25 return type;
26 }
17 - if (typeof type === 'function') {
18 - return type.displayName || type.name;
27 + switch (type) {
28 + case REACT_FRAGMENT_TYPE:
29 + return 'ReactFragment';
30 + case REACT_PORTAL_TYPE:
31 + return 'ReactPortal';
32 + case REACT_CALL_TYPE:
33 + return 'ReactCall';
34 + case REACT_RETURN_TYPE:
35 + return 'ReactReturn';
36 }
37 return null;
38 }