[react-core] Add experimental React Scope component API (#16587)

This commit is contained in:
Dominic Gannaway
2019-08-29 12:06:51 +01:00
committed by GitHub
parent 996acf9036
commit bd79be9b68
22 changed files with 459 additions and 94 deletions

View File

@@ -12,7 +12,7 @@ import {
PASSIVE_NOT_SUPPORTED,
} from 'legacy-events/EventSystemFlags';
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
import {HostComponent, SuspenseComponent} from 'shared/ReactWorkTags';
import {HostComponent} from 'shared/ReactWorkTags';
import type {EventPriority} from 'shared/ReactTypes';
import type {
ReactDOMEventResponder,
@@ -238,28 +238,6 @@ const eventResponderContext: ReactDOMResponderContext = {
}
}
},
getFocusableElementsInScope(deep: boolean): Array<HTMLElement> {
validateResponderContext();
const focusableElements = [];
const eventResponderInstance = ((currentInstance: any): ReactDOMEventResponderInstance);
const currentResponder = eventResponderInstance.responder;
let focusScopeFiber = eventResponderInstance.fiber;
if (deep) {
let deepNode = focusScopeFiber.return;
while (deepNode !== null) {
if (doesFiberHaveResponder(deepNode, currentResponder)) {
focusScopeFiber = deepNode;
}
deepNode = deepNode.return;
}
}
const child = focusScopeFiber.child;
if (child !== null) {
collectFocusableElements(child, focusableElements);
}
return focusableElements;
},
getActiveDocument,
objectAssign: Object.assign,
getTimeStamp(): number {
@@ -335,33 +313,6 @@ function validateEventValue(eventValue: any): void {
}
}
function collectFocusableElements(
node: Fiber,
focusableElements: Array<HTMLElement>,
): void {
if (isFiberSuspenseAndTimedOut(node)) {
const fallbackChild = getSuspenseFallbackChild(node);
if (fallbackChild !== null) {
collectFocusableElements(fallbackChild, focusableElements);
}
} else {
if (isFiberHostComponentFocusable(node)) {
focusableElements.push(node.stateNode);
} else {
const child = node.child;
if (child !== null) {
collectFocusableElements(child, focusableElements);
}
}
}
const sibling = node.sibling;
if (sibling !== null) {
collectFocusableElements(sibling, focusableElements);
}
}
function doesFiberHaveResponder(
fiber: Fiber,
responder: ReactDOMEventResponder,
@@ -382,33 +333,6 @@ function getActiveDocument(): Document {
return ((currentDocument: any): Document);
}
function isFiberHostComponentFocusable(fiber: Fiber): boolean {
if (fiber.tag !== HostComponent) {
return false;
}
const {type, memoizedProps} = fiber;
if (memoizedProps.tabIndex === -1 || memoizedProps.disabled) {
return false;
}
if (memoizedProps.tabIndex === 0 || memoizedProps.contentEditable === true) {
return true;
}
if (type === 'a' || type === 'area') {
return !!memoizedProps.href && memoizedProps.rel !== 'ignore';
}
if (type === 'input') {
return memoizedProps.type !== 'hidden' && memoizedProps.type !== 'file';
}
return (
type === 'button' ||
type === 'textarea' ||
type === 'object' ||
type === 'select' ||
type === 'iframe' ||
type === 'embed'
);
}
function processTimers(
timers: Map<number, ResponderTimer>,
delay: number,
@@ -626,14 +550,6 @@ function validateResponderContext(): void {
);
}
function isFiberSuspenseAndTimedOut(fiber: Fiber): boolean {
return fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
}
function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
return ((((fiber.child: any): Fiber).sibling: any): Fiber).child;
}
export function dispatchEventForResponderEventSystem(
topLevelType: string,
targetFiber: null | Fiber,

View File

@@ -92,11 +92,6 @@ const event = { type: 'press', target, pointerType, x, y };
context.dispatchEvent('onPress', event, DiscreteEvent);
```
### getFocusableElementsInScope(): Array<Element>
Returns every DOM element that can be focused within the scope of the Event
Responder instance.
### isTargetWithinNode(target: Element, element: Element): boolean
Returns `true` if `target` is a child of `element`.

View File

@@ -15,6 +15,7 @@ import type {
ReactEventResponder,
ReactEventResponderInstance,
ReactFundamentalComponent,
ReactScope,
} from 'shared/ReactTypes';
import type {RootTag} from 'shared/ReactRootTags';
import type {WorkTag} from 'shared/ReactWorkTags';
@@ -32,6 +33,7 @@ import {
enableProfilerTimer,
enableFundamentalAPI,
enableUserTimingAPI,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import {NoEffect, Placement} from 'shared/ReactSideEffectTags';
import {ConcurrentRoot, BatchedRoot} from 'shared/ReactRootTags';
@@ -56,6 +58,7 @@ import {
SimpleMemoComponent,
LazyComponent,
FundamentalComponent,
ScopeComponent,
} from 'shared/ReactWorkTags';
import getComponentName from 'shared/getComponentName';
@@ -86,6 +89,7 @@ import {
REACT_MEMO_TYPE,
REACT_LAZY_TYPE,
REACT_FUNDAMENTAL_TYPE,
REACT_SCOPE_TYPE,
} from 'shared/ReactSymbols';
let hasBadMapPolyfill;
@@ -674,6 +678,16 @@ export function createFiberFromTypeAndProps(
);
}
break;
case REACT_SCOPE_TYPE:
if (enableScopeAPI) {
return createFiberFromScope(
type,
pendingProps,
mode,
expirationTime,
key,
);
}
}
}
let info = '';
@@ -766,6 +780,19 @@ export function createFiberFromFundamental(
return fiber;
}
function createFiberFromScope(
scope: ReactScope,
pendingProps: any,
mode: TypeOfMode,
expirationTime: ExpirationTime,
key: null | string,
) {
const fiber = createFiber(ScopeComponent, pendingProps, key, mode);
fiber.type = scope;
fiber.expirationTime = expirationTime;
return fiber;
}
function createFiberFromProfiler(
pendingProps: any,
mode: TypeOfMode,

View File

@@ -41,6 +41,7 @@ import {
LazyComponent,
IncompleteClassComponent,
FundamentalComponent,
ScopeComponent,
} from 'shared/ReactWorkTags';
import {
NoEffect,
@@ -63,6 +64,7 @@ import {
enableSuspenseServerRenderer,
enableFundamentalAPI,
warnAboutDefaultPropsOnFunctionComponents,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import invariant from 'shared/invariant';
import shallowEqual from 'shared/shallowEqual';
@@ -2672,6 +2674,19 @@ function updateFundamentalComponent(
return workInProgress.child;
}
function updateScopeComponent(current, workInProgress, renderExpirationTime) {
const nextProps = workInProgress.pendingProps;
const nextChildren = nextProps.children;
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}
export function markWorkInProgressReceivedUpdate() {
didReceiveUpdate = true;
}
@@ -3158,6 +3173,16 @@ function beginWork(
}
break;
}
case ScopeComponent: {
if (enableScopeAPI) {
return updateScopeComponent(
current,
workInProgress,
renderExpirationTime,
);
}
break;
}
}
invariant(
false,

View File

@@ -32,6 +32,7 @@ import {
enableFlareAPI,
enableFundamentalAPI,
enableSuspenseCallback,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
@@ -49,6 +50,7 @@ import {
SimpleMemoComponent,
SuspenseListComponent,
FundamentalComponent,
ScopeComponent,
} from 'shared/ReactWorkTags';
import {
invokeGuardedCallback,
@@ -617,6 +619,7 @@ function commitLifeCycles(
case SuspenseListComponent:
case IncompleteClassComponent:
case FundamentalComponent:
case ScopeComponent:
return;
default: {
invariant(
@@ -691,6 +694,10 @@ function commitAttachRef(finishedWork: Fiber) {
default:
instanceToUse = instance;
}
// Moved outside to ensure DCE works with this flag
if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
instanceToUse = instance.methods;
}
if (typeof ref === 'function') {
ref(instanceToUse);
} else {
@@ -1383,6 +1390,13 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
}
return;
}
case ScopeComponent: {
if (enableScopeAPI) {
const scopeInstance = finishedWork.stateNode;
scopeInstance.fiber = finishedWork;
}
return;
}
default: {
invariant(
false,

View File

@@ -9,7 +9,10 @@
import type {Fiber} from './ReactFiber';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {ReactFundamentalComponentInstance} from 'shared/ReactTypes';
import type {
ReactFundamentalComponentInstance,
ReactScopeInstance,
} from 'shared/ReactTypes';
import type {FiberRoot} from './ReactFiberRoot';
import type {
Instance,
@@ -47,6 +50,7 @@ import {
LazyComponent,
IncompleteClassComponent,
FundamentalComponent,
ScopeComponent,
} from 'shared/ReactWorkTags';
import {NoMode, BatchedMode} from './ReactTypeOfMode';
import {
@@ -112,6 +116,7 @@ import {
enableSuspenseServerRenderer,
enableFlareAPI,
enableFundamentalAPI,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import {
markSpawnedWork,
@@ -123,6 +128,7 @@ import {createFundamentalStateInstance} from './ReactFiberFundamental';
import {Never} from './ReactFiberExpirationTime';
import {resetChildFibers} from './ReactChildFiber';
import {updateEventListeners} from './ReactFiberEvents';
import {createScopeMethods} from './ReactFiberScope';
function markUpdate(workInProgress: Fiber) {
// Tag the fiber with an update effect. This turns a Placement into
@@ -1213,6 +1219,31 @@ function completeWork(
}
break;
}
case ScopeComponent: {
if (enableScopeAPI) {
if (current === null) {
const type = workInProgress.type;
const scopeInstance: ReactScopeInstance = {
fiber: workInProgress,
methods: null,
};
workInProgress.stateNode = scopeInstance;
scopeInstance.methods = createScopeMethods(type, scopeInstance);
if (workInProgress.ref !== null) {
markRef(workInProgress);
markUpdate(workInProgress);
}
} else {
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
if (workInProgress.ref !== null) {
markUpdate(workInProgress);
}
}
}
break;
}
default:
invariant(
false,

View File

@@ -0,0 +1,130 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {Fiber} from './ReactFiber';
import type {
ReactScope,
ReactScopeInstance,
ReactScopeMethods,
} from 'shared/ReactTypes';
import {
HostComponent,
SuspenseComponent,
ScopeComponent,
} from 'shared/ReactWorkTags';
function isFiberSuspenseAndTimedOut(fiber: Fiber): boolean {
return fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
}
function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
return ((((fiber.child: any): Fiber).sibling: any): Fiber).child;
}
function collectScopedNodes(
node: Fiber,
fn: (type: string | Object, props: Object) => boolean,
scopedNodes: Array<any>,
): void {
if (node.tag === HostComponent) {
const {type, memoizedProps} = node;
if (fn(type, memoizedProps) === true) {
scopedNodes.push(node.stateNode);
}
}
let child = node.child;
if (isFiberSuspenseAndTimedOut(node)) {
child = getSuspenseFallbackChild(node);
}
if (child !== null) {
collectScopedNodesFromChildren(child, fn, scopedNodes);
}
}
function collectScopedNodesFromChildren(
startingChild: Fiber,
fn: (type: string | Object, props: Object) => boolean,
scopedNodes: Array<any>,
): void {
let child = startingChild;
while (child !== null) {
collectScopedNodes(child, fn, scopedNodes);
child = child.sibling;
}
}
function collectNearestScopeMethods(
node: Fiber,
scope: ReactScope,
childrenScopes: Array<ReactScopeMethods>,
): void {
if (node.tag === ScopeComponent && node.type === scope) {
childrenScopes.push(node.stateNode.methods);
} else {
let child = node.child;
if (isFiberSuspenseAndTimedOut(node)) {
child = getSuspenseFallbackChild(node);
}
if (child !== null) {
collectNearestChildScopeMethods(child, scope, childrenScopes);
}
}
}
function collectNearestChildScopeMethods(
startingChild: Fiber,
scope: ReactScope,
childrenScopes: Array<ReactScopeMethods>,
): void {
let child = startingChild;
while (child !== null) {
collectNearestScopeMethods(child, scope, childrenScopes);
child = child.sibling;
}
}
export function createScopeMethods(
scope: ReactScope,
instance: ReactScopeInstance,
): ReactScopeMethods {
const fn = scope.fn;
return {
getChildren(): null | Array<ReactScopeMethods> {
const currentFiber = ((instance.fiber: any): Fiber);
const child = currentFiber.child;
const childrenScopes = [];
if (child !== null) {
collectNearestChildScopeMethods(child, scope, childrenScopes);
}
return childrenScopes.length === 0 ? null : childrenScopes;
},
getParent(): null | ReactScopeMethods {
let node = ((instance.fiber: any): Fiber).return;
while (node !== null) {
if (node.tag === ScopeComponent && node.type === scope) {
return node.stateNode.methods;
}
node = node.return;
}
return null;
},
getScopedNodes(): null | Array<Object> {
const currentFiber = ((instance.fiber: any): Fiber);
const child = currentFiber.child;
const scopedNodes = [];
if (child !== null) {
collectScopedNodesFromChildren(child, fn, scopedNodes);
}
return scopedNodes.length === 0 ? null : scopedNodes;
},
};
}

View File

@@ -0,0 +1,168 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
let React;
let ReactFeatureFlags;
describe('ReactScope', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableScopeAPI = true;
React = require('react');
});
describe('ReactDOM', () => {
let ReactDOM;
let container;
beforeEach(() => {
ReactDOM = require('react-dom');
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('getScopedNodes() works as intended', () => {
const TestScope = React.unstable_createScope((type, props) => true);
const scopeRef = React.createRef();
const divRef = React.createRef();
const spanRef = React.createRef();
const aRef = React.createRef();
function Test({toggle}) {
return toggle ? (
<TestScope ref={scopeRef}>
<div ref={divRef}>DIV</div>
<span ref={spanRef}>SPAN</span>
<a ref={aRef}>A</a>
</TestScope>
) : (
<TestScope ref={scopeRef}>
<a ref={aRef}>A</a>
<div ref={divRef}>DIV</div>
<span ref={spanRef}>SPAN</span>
</TestScope>
);
}
ReactDOM.render(<Test toggle={true} />, container);
let nodes = scopeRef.current.getScopedNodes();
expect(nodes).toEqual([divRef.current, spanRef.current, aRef.current]);
ReactDOM.render(<Test toggle={false} />, container);
nodes = scopeRef.current.getScopedNodes();
expect(nodes).toEqual([aRef.current, divRef.current, spanRef.current]);
});
it('mixed getParent() and getScopedNodes() works as intended', () => {
const TestScope = React.unstable_createScope((type, props) => true);
const TestScope2 = React.unstable_createScope((type, props) => true);
const refA = React.createRef();
const refB = React.createRef();
const refC = React.createRef();
const refD = React.createRef();
const spanA = React.createRef();
const spanB = React.createRef();
const divA = React.createRef();
const divB = React.createRef();
function Test() {
return (
<div>
<TestScope ref={refA}>
<span ref={spanA}>
<TestScope2 ref={refB}>
<div ref={divA}>
<TestScope ref={refC}>
<span ref={spanB}>
<TestScope2 ref={refD}>
<div ref={divB}>>Hello world</div>
</TestScope2>
</span>
</TestScope>
</div>
</TestScope2>
</span>
</TestScope>
</div>
);
}
ReactDOM.render(<Test />, container);
const dParent = refD.current.getParent();
expect(dParent).not.toBe(null);
expect(dParent.getScopedNodes()).toEqual([
divA.current,
spanB.current,
divB.current,
]);
const cParent = refC.current.getParent();
expect(cParent).not.toBe(null);
expect(cParent.getScopedNodes()).toEqual([
spanA.current,
divA.current,
spanB.current,
divB.current,
]);
expect(refB.current.getParent()).toBe(null);
expect(refA.current.getParent()).toBe(null);
});
it('getChildren() works as intended', () => {
const TestScope = React.unstable_createScope((type, props) => true);
const TestScope2 = React.unstable_createScope((type, props) => true);
const refA = React.createRef();
const refB = React.createRef();
const refC = React.createRef();
const refD = React.createRef();
const spanA = React.createRef();
const spanB = React.createRef();
const divA = React.createRef();
const divB = React.createRef();
function Test() {
return (
<div>
<TestScope ref={refA}>
<span ref={spanA}>
<TestScope2 ref={refB}>
<div ref={divA}>
<TestScope ref={refC}>
<span ref={spanB}>
<TestScope2 ref={refD}>
<div ref={divB}>>Hello world</div>
</TestScope2>
</span>
</TestScope>
</div>
</TestScope2>
</span>
</TestScope>
</div>
);
}
ReactDOM.render(<Test />, container);
const dChildren = refD.current.getChildren();
expect(dChildren).toBe(null);
const cChildren = refC.current.getChildren();
expect(cChildren).toBe(null);
const bChildren = refB.current.getChildren();
expect(bChildren).toEqual([refD.current]);
const aChildren = refA.current.getChildren();
expect(aChildren).toEqual([refC.current]);
});
});
});

View File

@@ -53,10 +53,12 @@ import {
import ReactSharedInternals from './ReactSharedInternals';
import createFundamental from 'shared/createFundamentalComponent';
import createResponder from 'shared/createEventResponder';
import createScope from 'shared/createScope';
import {
enableJSXTransformAPI,
enableFlareAPI,
enableFundamentalAPI,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
const React = {
Children: {
@@ -114,6 +116,10 @@ if (enableFundamentalAPI) {
React.unstable_createFundamental = createFundamental;
}
if (enableScopeAPI) {
React.unstable_createScope = createScope;
}
// Note: some APIs are added with feature flags.
// Make sure that stable builds for open source
// don't modify the React object to avoid deopts.

View File

@@ -66,7 +66,6 @@ export type ReactDOMResponderContext = {
removeRootEventTypes: (rootEventTypes: Array<string>) => void,
setTimeout: (func: () => void, timeout: number) => number,
clearTimeout: (timerId: number) => void,
getFocusableElementsInScope(deep: boolean): Array<HTMLElement>,
getActiveDocument(): Document,
objectAssign: Function,
getTimeStamp: () => number,

View File

@@ -65,6 +65,9 @@ export const enableFlareAPI = false;
// Experimental Host Component support.
export const enableFundamentalAPI = false;
// Experimental Scope support.
export const enableScopeAPI = false;
// New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107
export const enableJSXTransformAPI = false;

View File

@@ -57,6 +57,7 @@ export const REACT_FUNDAMENTAL_TYPE = hasSymbol
export const REACT_RESPONDER_TYPE = hasSymbol
? Symbol.for('react.responder')
: 0xead6;
export const REACT_SCOPE_TYPE = hasSymbol ? Symbol.for('react.scope') : 0xead7;
const MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
const FAUX_ITERATOR_SYMBOL = '@@iterator';

View File

@@ -159,3 +159,19 @@ export type ReactFundamentalComponent<C, H> = {|
$$typeof: Symbol | number,
impl: ReactFundamentalImpl<C, H>,
|};
export type ReactScope = {|
$$typeof: Symbol | number,
fn: (type: string | Object, props: Object) => boolean,
|};
export type ReactScopeMethods = {|
getChildren(): null | Array<ReactScopeMethods>,
getParent(): null | ReactScopeMethods,
getScopedNodes(): null | Array<Object>,
|};
export type ReactScopeInstance = {|
fiber: Object,
methods: null | ReactScopeMethods,
|};

View File

@@ -28,7 +28,8 @@ export type WorkTag =
| 17
| 18
| 19
| 20;
| 20
| 21;
export const FunctionComponent = 0;
export const ClassComponent = 1;
@@ -51,3 +52,4 @@ export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const FundamentalComponent = 20;
export const ScopeComponent = 21;

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @flow
*/
import type {ReactScope} from 'shared/ReactTypes';
import {REACT_SCOPE_TYPE} from 'shared/ReactSymbols';
export default function createScope(
fn: (type: string | Object, props: Object) => boolean,
): ReactScope {
const scopeComponent = {
$$typeof: REACT_SCOPE_TYPE,
fn,
};
if (__DEV__) {
Object.freeze(scopeComponent);
}
return scopeComponent;
}

View File

@@ -33,6 +33,7 @@ export const warnAboutDeprecatedLifecycles = true;
export const warnAboutDeprecatedSetNativeProps = true;
export const enableFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableJSXTransformAPI = false;
export const warnAboutUnmockedScheduler = true;
export const flushSuspenseFallbacksInTests = true;

View File

@@ -28,6 +28,7 @@ export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableJSXTransformAPI = false;
export const warnAboutUnmockedScheduler = false;
export const flushSuspenseFallbacksInTests = true;

View File

@@ -28,6 +28,7 @@ export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableJSXTransformAPI = false;
export const warnAboutUnmockedScheduler = true;
export const flushSuspenseFallbacksInTests = true;

View File

@@ -28,6 +28,7 @@ export const enableSchedulerDebugging = false;
export const warnAboutDeprecatedSetNativeProps = false;
export const enableFlareAPI = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableJSXTransformAPI = false;
export const warnAboutUnmockedScheduler = false;
export const flushSuspenseFallbacksInTests = true;

View File

@@ -26,6 +26,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
export const disableJavaScriptURLs = false;
export const enableFlareAPI = true;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableJSXTransformAPI = true;
export const warnAboutUnmockedScheduler = true;
export const flushSuspenseFallbacksInTests = true;

View File

@@ -73,6 +73,8 @@ export const enableFlareAPI = true;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableJSXTransformAPI = true;
export const warnAboutUnmockedScheduler = true;

View File

@@ -21,6 +21,7 @@ import {
REACT_LAZY_TYPE,
REACT_FUNDAMENTAL_TYPE,
REACT_RESPONDER_TYPE,
REACT_SCOPE_TYPE,
} from 'shared/ReactSymbols';
export default function isValidElementType(type: mixed) {
@@ -42,6 +43,7 @@ export default function isValidElementType(type: mixed) {
type.$$typeof === REACT_CONTEXT_TYPE ||
type.$$typeof === REACT_FORWARD_REF_TYPE ||
type.$$typeof === REACT_FUNDAMENTAL_TYPE ||
type.$$typeof === REACT_RESPONDER_TYPE))
type.$$typeof === REACT_RESPONDER_TYPE ||
type.$$typeof === REACT_SCOPE_TYPE))
);
}