From b77b12311f0c66aad9b50f805c53dcc05d2ea75c Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Tue, 12 Dec 2017 15:04:40 -0800 Subject: [PATCH] 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 --- .../react-call-return/src/ReactCallReturn.js | 45 ++-- .../src/server/ReactPartialRenderer.js | 58 +++-- .../react-reconciler/src/ReactChildFiber.js | 231 +----------------- .../src/ReactDebugFiberPerf.js | 2 + packages/react-reconciler/src/ReactFiber.js | 136 ++++++----- .../src/ReactFiberBeginWork.js | 11 +- .../src/ReactFiberCompleteWork.js | 13 +- ...ReactIncrementalPerf-test.internal.js.snap | 7 +- packages/react/src/ReactChildren.js | 4 - .../react/src/__tests__/ReactChildren-test.js | 6 +- packages/shared/ReactTypes.js | 27 +- packages/shared/getComponentName.js | 21 +- 12 files changed, 195 insertions(+), 366 deletions(-) diff --git a/packages/react-call-return/src/ReactCallReturn.js b/packages/react-call-return/src/ReactCallReturn.js index d0d0781c93009..432ff8f192223 100644 --- a/packages/react-call-return/src/ReactCallReturn.js +++ b/packages/react-call-return/src/ReactCallReturn.js @@ -7,25 +7,33 @@ * @flow */ -import {REACT_CALL_TYPE, REACT_RETURN_TYPE} from 'shared/ReactSymbols'; +import { + REACT_CALL_TYPE, + REACT_RETURN_TYPE, + REACT_ELEMENT_TYPE, +} from 'shared/ReactSymbols'; import type {ReactCall, ReactNodeList, ReactReturn} from 'shared/ReactTypes'; -type CallHandler = (props: T, returns: Array) => ReactNodeList; +type CallHandler = (props: T, returns: Array) => ReactNodeList; -export function unstable_createCall( - children: mixed, - handler: CallHandler, +export function unstable_createCall( + children: ReactNodeList, + handler: CallHandler, props: T, key: ?string = null, -): ReactCall { +): ReactCall { const call = { // This tag allow us to uniquely identify this as a React Call - $$typeof: REACT_CALL_TYPE, + $$typeof: REACT_ELEMENT_TYPE, + type: REACT_CALL_TYPE, key: key == null ? null : '' + key, - children: children, - handler: handler, - props: props, + ref: null, + props: { + props, + handler, + children: children, + }, }; if (__DEV__) { @@ -39,11 +47,16 @@ export function unstable_createCall( return call; } -export function unstable_createReturn(value: mixed): ReactReturn { +export function unstable_createReturn(value: V): ReactReturn { const returnNode = { - // This tag allow us to uniquely identify this as a React Return - $$typeof: REACT_RETURN_TYPE, - value: value, + // This tag allow us to uniquely identify this as a React Call + $$typeof: REACT_ELEMENT_TYPE, + type: REACT_RETURN_TYPE, + key: null, + ref: null, + props: { + value, + }, }; if (__DEV__) { @@ -63,7 +76,7 @@ export function unstable_isCall(object: mixed): boolean { return ( typeof object === 'object' && object !== null && - object.$$typeof === REACT_CALL_TYPE + object.type === REACT_CALL_TYPE ); } @@ -74,7 +87,7 @@ export function unstable_isReturn(object: mixed): boolean { return ( typeof object === 'object' && object !== null && - object.$$typeof === REACT_RETURN_TYPE + object.type === REACT_RETURN_TYPE ); } diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 81fff65332b4b..cc939b691b073 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -621,11 +621,6 @@ class ReactDOMServerRenderer { 'Portals are not currently supported by the server renderer. ' + 'Render them conditionally so that they only appear on the client render.', ); - invariant( - $$typeof !== REACT_CALL_TYPE && $$typeof !== REACT_RETURN_TYPE, - 'The experimental Call and Return types are not currently ' + - 'supported by the server renderer.', - ); // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type. invariant( false, @@ -647,28 +642,37 @@ class ReactDOMServerRenderer { } this.stack.push(frame); return ''; - } else if ( - ((nextChild: any): ReactElement).type === REACT_FRAGMENT_TYPE - ) { - const nextChildren = toArray( - ((nextChild: any): ReactElement).props.children, - ); - const frame: Frame = { - domNamespace: parentNamespace, - children: nextChildren, - childIndex: 0, - context: context, - footer: '', - }; - if (__DEV__) { - ((frame: any): FrameDev).debugElementStack = []; - } - this.stack.push(frame); - return ''; - } else { - // Safe because we just checked it's an element. - const nextElement = ((nextChild: any): ReactElement); - return this.renderDOM(nextElement, context, parentNamespace); + } + // Safe because we just checked it's an element. + const nextElement = ((nextChild: any): ReactElement); + const elementType = nextElement.type; + switch (elementType) { + case REACT_FRAGMENT_TYPE: + const nextChildren = toArray( + ((nextChild: any): ReactElement).props.children, + ); + const frame: Frame = { + domNamespace: parentNamespace, + children: nextChildren, + childIndex: 0, + context: context, + footer: '', + }; + if (__DEV__) { + ((frame: any): FrameDev).debugElementStack = []; + } + this.stack.push(frame); + return ''; + case REACT_CALL_TYPE: + case REACT_RETURN_TYPE: + invariant( + false, + 'The experimental Call and Return types are not currently ' + + 'supported by the server renderer.', + ); + // eslint-disable-next-line-no-fallthrough + default: + return this.renderDOM(nextElement, context, parentNamespace); } } } diff --git a/packages/react-reconciler/src/ReactChildFiber.js b/packages/react-reconciler/src/ReactChildFiber.js index 69af9328bb8e7..8342a028c23ac 100644 --- a/packages/react-reconciler/src/ReactChildFiber.js +++ b/packages/react-reconciler/src/ReactChildFiber.js @@ -8,7 +8,7 @@ */ import type {ReactElement} from 'shared/ReactElementType'; -import type {ReactCall, ReactPortal, ReactReturn} from 'shared/ReactTypes'; +import type {ReactPortal} from 'shared/ReactTypes'; import type {Fiber} from 'react-reconciler/src/ReactFiber'; import type {ExpirationTime} from 'react-reconciler/src/ReactFiberExpirationTime'; @@ -17,8 +17,6 @@ import { getIteratorFn, REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, - REACT_CALL_TYPE, - REACT_RETURN_TYPE, REACT_PORTAL_TYPE, } from 'shared/ReactSymbols'; import { @@ -26,8 +24,6 @@ import { ClassComponent, HostText, HostPortal, - CallComponent, - ReturnComponent, Fragment, } from 'shared/ReactTypeOfWork'; import emptyObject from 'fbjs/lib/emptyObject'; @@ -39,8 +35,6 @@ import { createFiberFromElement, createFiberFromFragment, createFiberFromText, - createFiberFromCall, - createFiberFromReturn, createFiberFromPortal, } from './ReactFiber'; import ReactDebugCurrentFiber from './ReactDebugCurrentFiber'; @@ -363,55 +357,6 @@ function ChildReconciler(shouldTrackSideEffects) { } } - function updateCall( - returnFiber: Fiber, - current: Fiber | null, - call: ReactCall, - expirationTime: ExpirationTime, - ): Fiber { - // TODO: Should this also compare handler to determine whether to reuse? - if (current === null || current.tag !== CallComponent) { - // Insert - const created = createFiberFromCall( - call, - returnFiber.internalContextTag, - expirationTime, - ); - created.return = returnFiber; - return created; - } else { - // Move based on index - const existing = useFiber(current, call, expirationTime); - existing.return = returnFiber; - return existing; - } - } - - function updateReturn( - returnFiber: Fiber, - current: Fiber | null, - returnNode: ReactReturn, - expirationTime: ExpirationTime, - ): Fiber { - if (current === null || current.tag !== ReturnComponent) { - // Insert - const created = createFiberFromReturn( - returnNode, - returnFiber.internalContextTag, - expirationTime, - ); - created.type = returnNode.value; - created.return = returnFiber; - return created; - } else { - // Move based on index - const existing = useFiber(current, null, expirationTime); - existing.type = returnNode.value; - existing.return = returnFiber; - return existing; - } - } - function updatePortal( returnFiber: Fiber, current: Fiber | null, @@ -486,48 +431,15 @@ function ChildReconciler(shouldTrackSideEffects) { if (typeof newChild === 'object' && newChild !== null) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: { - if (newChild.type === REACT_FRAGMENT_TYPE) { - const created = createFiberFromFragment( - newChild.props.children, - returnFiber.internalContextTag, - expirationTime, - newChild.key, - ); - created.return = returnFiber; - return created; - } else { - const created = createFiberFromElement( - newChild, - returnFiber.internalContextTag, - expirationTime, - ); - created.ref = coerceRef(null, newChild); - created.return = returnFiber; - return created; - } - } - - case REACT_CALL_TYPE: { - const created = createFiberFromCall( + const created = createFiberFromElement( newChild, returnFiber.internalContextTag, expirationTime, ); + created.ref = coerceRef(null, newChild); created.return = returnFiber; return created; } - - case REACT_RETURN_TYPE: { - const created = createFiberFromReturn( - newChild, - returnFiber.internalContextTag, - expirationTime, - ); - created.type = newChild.value; - created.return = returnFiber; - return created; - } - case REACT_PORTAL_TYPE: { const created = createFiberFromPortal( newChild, @@ -610,31 +522,6 @@ function ChildReconciler(shouldTrackSideEffects) { return null; } } - - case REACT_CALL_TYPE: { - if (newChild.key === key) { - return updateCall(returnFiber, oldFiber, newChild, expirationTime); - } else { - return null; - } - } - - case REACT_RETURN_TYPE: { - // Returns don't have keys. If the previous node is implicitly keyed - // we can continue to replace it without aborting even if it is not a - // yield. - if (key === null) { - return updateReturn( - returnFiber, - oldFiber, - newChild, - expirationTime, - ); - } else { - return null; - } - } - case REACT_PORTAL_TYPE: { if (newChild.key === key) { return updatePortal( @@ -717,32 +604,6 @@ function ChildReconciler(shouldTrackSideEffects) { expirationTime, ); } - - case REACT_CALL_TYPE: { - const matchedFiber = - existingChildren.get( - newChild.key === null ? newIdx : newChild.key, - ) || null; - return updateCall( - returnFiber, - matchedFiber, - newChild, - expirationTime, - ); - } - - case REACT_RETURN_TYPE: { - // Returns don't have keys, so we neither have to check the old nor - // new node for the key. If both are returns, they match. - const matchedFiber = existingChildren.get(newIdx) || null; - return updateReturn( - returnFiber, - matchedFiber, - newChild, - expirationTime, - ); - } - case REACT_PORTAL_TYPE: { const matchedFiber = existingChildren.get( @@ -793,7 +654,6 @@ function ChildReconciler(shouldTrackSideEffects) { } switch (child.$$typeof) { case REACT_ELEMENT_TYPE: - case REACT_CALL_TYPE: case REACT_PORTAL_TYPE: warnForMissingKey(child); const key = child.key; @@ -1252,72 +1112,6 @@ function ChildReconciler(shouldTrackSideEffects) { } } - function reconcileSingleCall( - returnFiber: Fiber, - currentFirstChild: Fiber | null, - call: ReactCall, - expirationTime: ExpirationTime, - ): Fiber { - const key = call.key; - let child = currentFirstChild; - while (child !== null) { - // TODO: If key === null and child.key === null, then this only applies to - // the first item in the list. - if (child.key === key) { - if (child.tag === CallComponent) { - deleteRemainingChildren(returnFiber, child.sibling); - const existing = useFiber(child, call, expirationTime); - existing.return = returnFiber; - return existing; - } else { - deleteRemainingChildren(returnFiber, child); - break; - } - } else { - deleteChild(returnFiber, child); - } - child = child.sibling; - } - - const created = createFiberFromCall( - call, - returnFiber.internalContextTag, - expirationTime, - ); - created.return = returnFiber; - return created; - } - - function reconcileSingleReturn( - returnFiber: Fiber, - currentFirstChild: Fiber | null, - returnNode: ReactReturn, - expirationTime: ExpirationTime, - ): Fiber { - // There's no need to check for keys on yields since they're stateless. - let child = currentFirstChild; - if (child !== null) { - if (child.tag === ReturnComponent) { - deleteRemainingChildren(returnFiber, child.sibling); - const existing = useFiber(child, null, expirationTime); - existing.type = returnNode.value; - existing.return = returnFiber; - return existing; - } else { - deleteRemainingChildren(returnFiber, child); - } - } - - const created = createFiberFromReturn( - returnNode, - returnFiber.internalContextTag, - expirationTime, - ); - created.type = returnNode.value; - created.return = returnFiber; - return created; - } - function reconcileSinglePortal( returnFiber: Fiber, currentFirstChild: Fiber | null, @@ -1402,25 +1196,6 @@ function ChildReconciler(shouldTrackSideEffects) { expirationTime, ), ); - - case REACT_CALL_TYPE: - return placeSingleChild( - reconcileSingleCall( - returnFiber, - currentFirstChild, - newChild, - expirationTime, - ), - ); - case REACT_RETURN_TYPE: - return placeSingleChild( - reconcileSingleReturn( - returnFiber, - currentFirstChild, - newChild, - expirationTime, - ), - ); case REACT_PORTAL_TYPE: return placeSingleChild( reconcileSinglePortal( diff --git a/packages/react-reconciler/src/ReactDebugFiberPerf.js b/packages/react-reconciler/src/ReactDebugFiberPerf.js index e068baf946a1c..e4bb8053e3c1d 100644 --- a/packages/react-reconciler/src/ReactDebugFiberPerf.js +++ b/packages/react-reconciler/src/ReactDebugFiberPerf.js @@ -16,6 +16,7 @@ import { HostComponent, HostText, HostPortal, + CallComponent, ReturnComponent, Fragment, } from 'shared/ReactTypeOfWork'; @@ -166,6 +167,7 @@ const shouldIgnoreFiber = (fiber: Fiber): boolean => { case HostComponent: case HostText: case HostPortal: + case CallComponent: case ReturnComponent: case Fragment: return true; diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index bda4d89639d90..9d7c305d6fcfb 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -7,12 +7,7 @@ */ import type {ReactElement, Source} from 'shared/ReactElementType'; -import type { - ReactCall, - ReactFragment, - ReactPortal, - ReactReturn, -} from 'shared/ReactTypes'; +import type {ReactFragment, ReactPortal} from 'shared/ReactTypes'; import type {TypeOfWork} from 'shared/ReactTypeOfWork'; import type {TypeOfInternalContext} from './ReactTypeOfInternalContext'; import type {TypeOfSideEffect} from 'shared/ReactTypeOfSideEffect'; @@ -36,6 +31,11 @@ import getComponentName from 'shared/getComponentName'; import {NoWork} from './ReactFiberExpirationTime'; import {NoContext, AsyncUpdates} from './ReactTypeOfInternalContext'; +import { + REACT_FRAGMENT_TYPE, + REACT_RETURN_TYPE, + REACT_CALL_TYPE, +} from 'shared/ReactSymbols'; let hasBadMapPolyfill; @@ -324,44 +324,79 @@ export function createFiberFromElement( } else if (typeof type === 'string') { fiber = createFiber(HostComponent, pendingProps, key, internalContextTag); fiber.type = type; - } else if ( - typeof type === 'object' && - type !== null && - typeof type.tag === 'number' - ) { - // Currently assumed to be a continuation and therefore is a fiber already. - // TODO: The yield system is currently broken for updates in some cases. - // The reified yield stores a fiber, but we don't know which fiber that is; - // the current or a workInProgress? When the continuation gets rendered here - // we don't know if we can reuse that fiber or if we need to clone it. - // There is probably a clever way to restructure this. - fiber = ((type: any): Fiber); - fiber.pendingProps = pendingProps; } else { - let info = ''; - if (__DEV__) { - if ( - type === undefined || - (typeof type === 'object' && + switch (type) { + case REACT_FRAGMENT_TYPE: + return createFiberFromFragment( + pendingProps.children, + internalContextTag, + expirationTime, + key, + ); + case REACT_CALL_TYPE: + fiber = createFiber( + CallComponent, + pendingProps, + key, + internalContextTag, + ); + fiber.type = REACT_CALL_TYPE; + break; + case REACT_RETURN_TYPE: + fiber = createFiber( + ReturnComponent, + pendingProps, + key, + internalContextTag, + ); + fiber.type = REACT_RETURN_TYPE; + break; + default: { + if ( + typeof type === 'object' && type !== null && - Object.keys(type).length === 0) - ) { - info += - ' You likely forgot to export your component from the file ' + - "it's defined in, or you might have mixed up default and named imports."; - } - const ownerName = owner ? getComponentName(owner) : null; - if (ownerName) { - info += '\n\nCheck the render method of `' + ownerName + '`.'; + typeof type.tag === 'number' + ) { + // Currently assumed to be a continuation and therefore is a + // fiber already. + // TODO: The yield system is currently broken for updates in some + // cases. The reified yield stores a fiber, but we don't know which + // fiber that is; the current or a workInProgress? When the + // continuation gets rendered here we don't know if we can reuse that + // fiber or if we need to clone it. There is probably a clever way to + // restructure this. + fiber = ((type: any): Fiber); + fiber.pendingProps = pendingProps; + } else { + let info = ''; + if (__DEV__) { + if ( + type === undefined || + (typeof type === 'object' && + type !== null && + Object.keys(type).length === 0) + ) { + info += + ' You likely forgot to export your component from the file ' + + "it's defined in, or you might have mixed up default and " + + 'named imports.'; + } + const ownerName = owner ? getComponentName(owner) : null; + if (ownerName) { + info += '\n\nCheck the render method of `' + ownerName + '`.'; + } + } + invariant( + false, + 'Element type is invalid: expected a string (for built-in ' + + 'components) or a class/function (for composite components) ' + + 'but got: %s.%s', + type == null ? type : typeof type, + info, + ); + } } } - invariant( - false, - 'Element type is invalid: expected a string (for built-in components) ' + - 'or a class/function (for composite components) but got: %s.%s', - type == null ? type : typeof type, - info, - ); } if (__DEV__) { @@ -401,27 +436,6 @@ export function createFiberFromHostInstanceForDeletion(): Fiber { return fiber; } -export function createFiberFromCall( - call: ReactCall, - internalContextTag: TypeOfInternalContext, - expirationTime: ExpirationTime, -): Fiber { - const fiber = createFiber(CallComponent, call, call.key, internalContextTag); - fiber.type = call.handler; - fiber.expirationTime = expirationTime; - return fiber; -} - -export function createFiberFromReturn( - returnNode: ReactReturn, - internalContextTag: TypeOfInternalContext, - expirationTime: ExpirationTime, -): Fiber { - const fiber = createFiber(ReturnComponent, null, null, internalContextTag); - fiber.expirationTime = expirationTime; - return fiber; -} - export function createFiberFromPortal( portal: ReactPortal, internalContextTag: TypeOfInternalContext, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 124f4fb7496ec..42f53a28a0a5d 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -8,7 +8,6 @@ */ import type {HostConfig} from 'react-reconciler'; -import type {ReactCall} from 'shared/ReactTypes'; import type {Fiber} from 'react-reconciler/src/ReactFiber'; import type {HostContext} from './ReactFiberHostContext'; import type {HydrationContext} from './ReactFiberHydrationContext'; @@ -535,18 +534,18 @@ export default function( } function updateCallComponent(current, workInProgress, renderExpirationTime) { - let nextCall = (workInProgress.pendingProps: ReactCall); + let nextProps = workInProgress.pendingProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. - } else if (workInProgress.memoizedProps === nextCall) { - nextCall = workInProgress.memoizedProps; + } else if (workInProgress.memoizedProps === nextProps) { + nextProps = workInProgress.memoizedProps; // TODO: When bailing out, we might need to return the stateNode instead // of the child. To check it for work. // return bailoutOnAlreadyFinishedWork(current, workInProgress); } - const nextChildren = nextCall.children; + const nextChildren = nextProps.children; // The following is a fork of reconcileChildrenAtExpirationTime but using // stateNode to store the child. @@ -566,7 +565,7 @@ export default function( ); } - memoizeProps(workInProgress, nextCall); + memoizeProps(workInProgress, nextProps); // This doesn't take arbitrary time so we could synchronously just begin // eagerly do the work of workInProgress.child as an optimization. return workInProgress.stateNode; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 9e07f34b348a6..40bb9ea4f46c2 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -8,7 +8,6 @@ */ import type {HostConfig} from 'react-reconciler'; -import type {ReactCall} from 'shared/ReactTypes'; import type {Fiber} from './ReactFiber'; import type {ExpirationTime} from './ReactFiberExpirationTime'; import type {HostContext} from './ReactFiberHostContext'; @@ -93,7 +92,7 @@ export default function( ) { invariant(false, 'A call cannot have host component children.'); } else if (node.tag === ReturnComponent) { - returns.push(node.type); + returns.push(node.pendingProps.value); } else if (node.child !== null) { node.child.return = node; node = node.child; @@ -115,9 +114,9 @@ export default function( workInProgress: Fiber, renderExpirationTime: ExpirationTime, ) { - const call = (workInProgress.memoizedProps: ?ReactCall); + const props = workInProgress.memoizedProps; invariant( - call, + props, 'Should be resolved by now. This error is likely caused by a bug in ' + 'React. Please file an issue.', ); @@ -135,9 +134,9 @@ export default function( // TODO: Compare this to a generator or opaque helpers like Children. const returns: Array = []; appendAllReturns(returns, workInProgress); - const fn = call.handler; - const props = call.props; - const nextChildren = fn(props, returns); + const fn = props.handler; + const childProps = props.props; + const nextChildren = fn(childProps, returns); const currentFirstChild = current !== null ? current.child : null; workInProgress.child = reconcileChildFibers( diff --git a/packages/react-reconciler/src/__tests__/__snapshots__/ReactIncrementalPerf-test.internal.js.snap b/packages/react-reconciler/src/__tests__/__snapshots__/ReactIncrementalPerf-test.internal.js.snap index 1d588f3e7fef5..753ac38ef0744 100644 --- a/packages/react-reconciler/src/__tests__/__snapshots__/ReactIncrementalPerf-test.internal.js.snap +++ b/packages/react-reconciler/src/__tests__/__snapshots__/ReactIncrementalPerf-test.internal.js.snap @@ -298,10 +298,9 @@ exports[`ReactDebugFiberPerf supports returns 1`] = ` ⚛ (React Tree Reconciliation) ⚛ App [mount] ⚛ CoParent [mount] - ⚛ HandleReturns [mount] - ⚛ Indirection [mount] - ⚛ CoChild [mount] - ⚛ CoChild [mount] + ⚛ Indirection [mount] + ⚛ CoChild [mount] + ⚛ CoChild [mount] ⚛ Continuation [mount] ⚛ Continuation [mount] diff --git a/packages/react/src/ReactChildren.js b/packages/react/src/ReactChildren.js index efb9a2a84b36f..b44b833912894 100644 --- a/packages/react/src/ReactChildren.js +++ b/packages/react/src/ReactChildren.js @@ -11,8 +11,6 @@ import warning from 'fbjs/lib/warning'; import { getIteratorFn, REACT_ELEMENT_TYPE, - REACT_CALL_TYPE, - REACT_RETURN_TYPE, REACT_PORTAL_TYPE, } from 'shared/ReactSymbols'; @@ -125,8 +123,6 @@ function traverseAllChildrenImpl( case 'object': switch (children.$$typeof) { case REACT_ELEMENT_TYPE: - case REACT_CALL_TYPE: - case REACT_RETURN_TYPE: case REACT_PORTAL_TYPE: invokeCallback = true; } diff --git a/packages/react/src/__tests__/ReactChildren-test.js b/packages/react/src/__tests__/ReactChildren-test.js index 952807851c9b4..e9d4948f6c4e3 100644 --- a/packages/react/src/__tests__/ReactChildren-test.js +++ b/packages/react/src/__tests__/ReactChildren-test.js @@ -95,7 +95,8 @@ describe('ReactChildren', () => { context, ); expect(callback).toHaveBeenCalledWith(reactCall, 0); - expect(mappedChildren[0]).toEqual(reactCall); + expect(mappedChildren[0].type).toEqual(reactCall.type); + expect(mappedChildren[0].props).toEqual(reactCall.props); }); it('should support Return components', () => { @@ -119,7 +120,8 @@ describe('ReactChildren', () => { context, ); expect(callback).toHaveBeenCalledWith(reactReturn, 0); - expect(mappedChildren[0]).toEqual(reactReturn); + expect(mappedChildren[0].props).toEqual(reactReturn.props); + expect(mappedChildren[0].type).toEqual(reactReturn.type); }); it('should treat single arrayless child as being in array', () => { diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 900582e5d5ac8..c68864323d7cb 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -10,8 +10,8 @@ export type ReactNode = | React$Element - | ReactCall - | ReactReturn + | ReactCall + | ReactReturn | ReactPortal | ReactText | ReactFragment; @@ -24,18 +24,27 @@ export type ReactText = string | number; export type ReactEmpty = null | void | boolean; -export type ReactCall = { +export type ReactCall = { $$typeof: Symbol | number, + type: Symbol | number, key: null | string, - children: any, - // This should be a more specific CallHandler - handler: (props: any, returns: Array) => ReactNodeList, - props: any, + ref: null, + props: { + props: any, + // This should be a more specific CallHandler + handler: (props: any, returns: Array) => ReactNodeList, + children?: ReactNodeList, + }, }; -export type ReactReturn = { +export type ReactReturn = { $$typeof: Symbol | number, - value: mixed, + type: Symbol | number, + key: null, + ref: null, + props: { + value: V, + }, }; export type ReactPortal = { diff --git a/packages/shared/getComponentName.js b/packages/shared/getComponentName.js index 661a70904396c..e00bd89725162 100644 --- a/packages/shared/getComponentName.js +++ b/packages/shared/getComponentName.js @@ -9,13 +9,30 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber'; +import { + REACT_CALL_TYPE, + REACT_FRAGMENT_TYPE, + REACT_RETURN_TYPE, + REACT_PORTAL_TYPE, +} from 'shared/ReactSymbols'; + function getComponentName(fiber: Fiber): string | null { const {type} = fiber; + if (typeof type === 'function') { + return type.displayName || type.name; + } if (typeof type === 'string') { return type; } - if (typeof type === 'function') { - return type.displayName || type.name; + switch (type) { + case REACT_FRAGMENT_TYPE: + return 'ReactFragment'; + case REACT_PORTAL_TYPE: + return 'ReactPortal'; + case REACT_CALL_TYPE: + return 'ReactCall'; + case REACT_RETURN_TYPE: + return 'ReactReturn'; } return null; }