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

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

Dominic Gannaway committed Aug 29, 2019 at 12:06 UTC bd79be9b687156067416ffe5219e49a11bd0f1e7
22 files changed +459 -94
packages/react-dom/src/events/DOMEventResponderSystem.js
+1 -85
@@ -12,7 +12,7 @@ import {
12 PASSIVE_NOT_SUPPORTED,
13 } from 'legacy-events/EventSystemFlags';
14 import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
15 -import {HostComponent, SuspenseComponent} from 'shared/ReactWorkTags';
15 +import {HostComponent} from 'shared/ReactWorkTags';
16 import type {EventPriority} from 'shared/ReactTypes';
17 import type {
18 ReactDOMEventResponder,
@@ -238,28 +238,6 @@ const eventResponderContext: ReactDOMResponderContext = {
238 }
239 }
240 },
241 - getFocusableElementsInScope(deep: boolean): Array<HTMLElement> {
242 - validateResponderContext();
243 - const focusableElements = [];
244 - const eventResponderInstance = ((currentInstance: any): ReactDOMEventResponderInstance);
245 - const currentResponder = eventResponderInstance.responder;
246 - let focusScopeFiber = eventResponderInstance.fiber;
247 - if (deep) {
248 - let deepNode = focusScopeFiber.return;
249 - while (deepNode !== null) {
250 - if (doesFiberHaveResponder(deepNode, currentResponder)) {
251 - focusScopeFiber = deepNode;
252 - }
253 - deepNode = deepNode.return;
254 - }
255 - }
256 - const child = focusScopeFiber.child;
257 -
258 - if (child !== null) {
259 - collectFocusableElements(child, focusableElements);
260 - }
261 - return focusableElements;
262 - },
241 getActiveDocument,
242 objectAssign: Object.assign,
243 getTimeStamp(): number {
@@ -335,33 +313,6 @@ function validateEventValue(eventValue: any): void {
313 }
314 }
315
338 -function collectFocusableElements(
339 - node: Fiber,
340 - focusableElements: Array<HTMLElement>,
341 -): void {
342 - if (isFiberSuspenseAndTimedOut(node)) {
343 - const fallbackChild = getSuspenseFallbackChild(node);
344 - if (fallbackChild !== null) {
345 - collectFocusableElements(fallbackChild, focusableElements);
346 - }
347 - } else {
348 - if (isFiberHostComponentFocusable(node)) {
349 - focusableElements.push(node.stateNode);
350 - } else {
351 - const child = node.child;
352 -
353 - if (child !== null) {
354 - collectFocusableElements(child, focusableElements);
355 - }
356 - }
357 - }
358 - const sibling = node.sibling;
359 -
360 - if (sibling !== null) {
361 - collectFocusableElements(sibling, focusableElements);
362 - }
363 -}
364 -
316 function doesFiberHaveResponder(
317 fiber: Fiber,
318 responder: ReactDOMEventResponder,
@@ -382,33 +333,6 @@ function getActiveDocument(): Document {
333 return ((currentDocument: any): Document);
334 }
335
385 -function isFiberHostComponentFocusable(fiber: Fiber): boolean {
386 - if (fiber.tag !== HostComponent) {
387 - return false;
388 - }
389 - const {type, memoizedProps} = fiber;
390 - if (memoizedProps.tabIndex === -1 || memoizedProps.disabled) {
391 - return false;
392 - }
393 - if (memoizedProps.tabIndex === 0 || memoizedProps.contentEditable === true) {
394 - return true;
395 - }
396 - if (type === 'a' || type === 'area') {
397 - return !!memoizedProps.href && memoizedProps.rel !== 'ignore';
398 - }
399 - if (type === 'input') {
400 - return memoizedProps.type !== 'hidden' && memoizedProps.type !== 'file';
401 - }
402 - return (
403 - type === 'button' ||
404 - type === 'textarea' ||
405 - type === 'object' ||
406 - type === 'select' ||
407 - type === 'iframe' ||
408 - type === 'embed'
409 - );
410 -}
411 -
336 function processTimers(
337 timers: Map<number, ResponderTimer>,
338 delay: number,
@@ -626,14 +550,6 @@ function validateResponderContext(): void {
550 );
551 }
552
629 -function isFiberSuspenseAndTimedOut(fiber: Fiber): boolean {
630 - return fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
631 -}
632 -
633 -function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
634 - return ((((fiber.child: any): Fiber).sibling: any): Fiber).child;
635 -}
636 -
553 export function dispatchEventForResponderEventSystem(
554 topLevelType: string,
555 targetFiber: null | Fiber,
packages/react-events/README.md
-5
@@ -92,11 +92,6 @@ const event = { type: 'press', target, pointerType, x, y };
92 context.dispatchEvent('onPress', event, DiscreteEvent);
93 ```
94
95 -### getFocusableElementsInScope(): Array<Element>
96 -
97 -Returns every DOM element that can be focused within the scope of the Event
98 -Responder instance.
99 -
95 ### isTargetWithinNode(target: Element, element: Element): boolean
96
97 Returns `true` if `target` is a child of `element`.
packages/react-reconciler/src/ReactFiber.js
+27
@@ -15,6 +15,7 @@ import type {
15 ReactEventResponder,
16 ReactEventResponderInstance,
17 ReactFundamentalComponent,
18 + ReactScope,
19 } from 'shared/ReactTypes';
20 import type {RootTag} from 'shared/ReactRootTags';
21 import type {WorkTag} from 'shared/ReactWorkTags';
@@ -32,6 +33,7 @@ import {
33 enableProfilerTimer,
34 enableFundamentalAPI,
35 enableUserTimingAPI,
36 + enableScopeAPI,
37 } from 'shared/ReactFeatureFlags';
38 import {NoEffect, Placement} from 'shared/ReactSideEffectTags';
39 import {ConcurrentRoot, BatchedRoot} from 'shared/ReactRootTags';
@@ -56,6 +58,7 @@ import {
58 SimpleMemoComponent,
59 LazyComponent,
60 FundamentalComponent,
61 + ScopeComponent,
62 } from 'shared/ReactWorkTags';
63 import getComponentName from 'shared/getComponentName';
64
@@ -86,6 +89,7 @@ import {
89 REACT_MEMO_TYPE,
90 REACT_LAZY_TYPE,
91 REACT_FUNDAMENTAL_TYPE,
92 + REACT_SCOPE_TYPE,
93 } from 'shared/ReactSymbols';
94
95 let hasBadMapPolyfill;
@@ -674,6 +678,16 @@ export function createFiberFromTypeAndProps(
678 );
679 }
680 break;
681 + case REACT_SCOPE_TYPE:
682 + if (enableScopeAPI) {
683 + return createFiberFromScope(
684 + type,
685 + pendingProps,
686 + mode,
687 + expirationTime,
688 + key,
689 + );
690 + }
691 }
692 }
693 let info = '';
@@ -766,6 +780,19 @@ export function createFiberFromFundamental(
780 return fiber;
781 }
782
783 +function createFiberFromScope(
784 + scope: ReactScope,
785 + pendingProps: any,
786 + mode: TypeOfMode,
787 + expirationTime: ExpirationTime,
788 + key: null | string,
789 +) {
790 + const fiber = createFiber(ScopeComponent, pendingProps, key, mode);
791 + fiber.type = scope;
792 + fiber.expirationTime = expirationTime;
793 + return fiber;
794 +}
795 +
796 function createFiberFromProfiler(
797 pendingProps: any,
798 mode: TypeOfMode,
packages/react-reconciler/src/ReactFiberBeginWork.js
+25
@@ -41,6 +41,7 @@ import {
41 LazyComponent,
42 IncompleteClassComponent,
43 FundamentalComponent,
44 + ScopeComponent,
45 } from 'shared/ReactWorkTags';
46 import {
47 NoEffect,
@@ -63,6 +64,7 @@ import {
64 enableSuspenseServerRenderer,
65 enableFundamentalAPI,
66 warnAboutDefaultPropsOnFunctionComponents,
67 + enableScopeAPI,
68 } from 'shared/ReactFeatureFlags';
69 import invariant from 'shared/invariant';
70 import shallowEqual from 'shared/shallowEqual';
@@ -2672,6 +2674,19 @@ function updateFundamentalComponent(
2674 return workInProgress.child;
2675 }
2676
2677 +function updateScopeComponent(current, workInProgress, renderExpirationTime) {
2678 + const nextProps = workInProgress.pendingProps;
2679 + const nextChildren = nextProps.children;
2680 +
2681 + reconcileChildren(
2682 + current,
2683 + workInProgress,
2684 + nextChildren,
2685 + renderExpirationTime,
2686 + );
2687 + return workInProgress.child;
2688 +}
2689 +
2690 export function markWorkInProgressReceivedUpdate() {
2691 didReceiveUpdate = true;
2692 }
@@ -3158,6 +3173,16 @@ function beginWork(
3173 }
3174 break;
3175 }
3176 + case ScopeComponent: {
3177 + if (enableScopeAPI) {
3178 + return updateScopeComponent(
3179 + current,
3180 + workInProgress,
3181 + renderExpirationTime,
3182 + );
3183 + }
3184 + break;
3185 + }
3186 }
3187 invariant(
3188 false,
packages/react-reconciler/src/ReactFiberCommitWork.js
+14
@@ -32,6 +32,7 @@ import {
32 enableFlareAPI,
33 enableFundamentalAPI,
34 enableSuspenseCallback,
35 + enableScopeAPI,
36 } from 'shared/ReactFeatureFlags';
37 import {
38 FunctionComponent,
@@ -49,6 +50,7 @@ import {
50 SimpleMemoComponent,
51 SuspenseListComponent,
52 FundamentalComponent,
53 + ScopeComponent,
54 } from 'shared/ReactWorkTags';
55 import {
56 invokeGuardedCallback,
@@ -617,6 +619,7 @@ function commitLifeCycles(
619 case SuspenseListComponent:
620 case IncompleteClassComponent:
621 case FundamentalComponent:
622 + case ScopeComponent:
623 return;
624 default: {
625 invariant(
@@ -691,6 +694,10 @@ function commitAttachRef(finishedWork: Fiber) {
694 default:
695 instanceToUse = instance;
696 }
697 + // Moved outside to ensure DCE works with this flag
698 + if (enableScopeAPI && finishedWork.tag === ScopeComponent) {
699 + instanceToUse = instance.methods;
700 + }
701 if (typeof ref === 'function') {
702 ref(instanceToUse);
703 } else {
@@ -1383,6 +1390,13 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1390 }
1391 return;
1392 }
1393 + case ScopeComponent: {
1394 + if (enableScopeAPI) {
1395 + const scopeInstance = finishedWork.stateNode;
1396 + scopeInstance.fiber = finishedWork;
1397 + }
1398 + return;
1399 + }
1400 default: {
1401 invariant(
1402 false,
packages/react-reconciler/src/ReactFiberCompleteWork.js
+32 -1
@@ -9,7 +9,10 @@
9
10 import type {Fiber} from './ReactFiber';
11 import type {ExpirationTime} from './ReactFiberExpirationTime';
12 -import type {ReactFundamentalComponentInstance} from 'shared/ReactTypes';
12 +import type {
13 + ReactFundamentalComponentInstance,
14 + ReactScopeInstance,
15 +} from 'shared/ReactTypes';
16 import type {FiberRoot} from './ReactFiberRoot';
17 import type {
18 Instance,
@@ -47,6 +50,7 @@ import {
50 LazyComponent,
51 IncompleteClassComponent,
52 FundamentalComponent,
53 + ScopeComponent,
54 } from 'shared/ReactWorkTags';
55 import {NoMode, BatchedMode} from './ReactTypeOfMode';
56 import {
@@ -112,6 +116,7 @@ import {
116 enableSuspenseServerRenderer,
117 enableFlareAPI,
118 enableFundamentalAPI,
119 + enableScopeAPI,
120 } from 'shared/ReactFeatureFlags';
121 import {
122 markSpawnedWork,
@@ -123,6 +128,7 @@ import {createFundamentalStateInstance} from './ReactFiberFundamental';
128 import {Never} from './ReactFiberExpirationTime';
129 import {resetChildFibers} from './ReactChildFiber';
130 import {updateEventListeners} from './ReactFiberEvents';
131 +import {createScopeMethods} from './ReactFiberScope';
132
133 function markUpdate(workInProgress: Fiber) {
134 // Tag the fiber with an update effect. This turns a Placement into
@@ -1213,6 +1219,31 @@ function completeWork(
1219 }
1220 break;
1221 }
1222 + case ScopeComponent: {
1223 + if (enableScopeAPI) {
1224 + if (current === null) {
1225 + const type = workInProgress.type;
1226 + const scopeInstance: ReactScopeInstance = {
1227 + fiber: workInProgress,
1228 + methods: null,
1229 + };
1230 + workInProgress.stateNode = scopeInstance;
1231 + scopeInstance.methods = createScopeMethods(type, scopeInstance);
1232 + if (workInProgress.ref !== null) {
1233 + markRef(workInProgress);
1234 + markUpdate(workInProgress);
1235 + }
1236 + } else {
1237 + if (current.ref !== workInProgress.ref) {
1238 + markRef(workInProgress);
1239 + }
1240 + if (workInProgress.ref !== null) {
1241 + markUpdate(workInProgress);
1242 + }
1243 + }
1244 + }
1245 + break;
1246 + }
1247 default:
1248 invariant(
1249 false,
packages/react-reconciler/src/ReactFiberScope.js new
+130
@@ -0,0 +1,130 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {Fiber} from './ReactFiber';
11 +import type {
12 + ReactScope,
13 + ReactScopeInstance,
14 + ReactScopeMethods,
15 +} from 'shared/ReactTypes';
16 +
17 +import {
18 + HostComponent,
19 + SuspenseComponent,
20 + ScopeComponent,
21 +} from 'shared/ReactWorkTags';
22 +
23 +function isFiberSuspenseAndTimedOut(fiber: Fiber): boolean {
24 + return fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
25 +}
26 +
27 +function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
28 + return ((((fiber.child: any): Fiber).sibling: any): Fiber).child;
29 +}
30 +
31 +function collectScopedNodes(
32 + node: Fiber,
33 + fn: (type: string | Object, props: Object) => boolean,
34 + scopedNodes: Array<any>,
35 +): void {
36 + if (node.tag === HostComponent) {
37 + const {type, memoizedProps} = node;
38 + if (fn(type, memoizedProps) === true) {
39 + scopedNodes.push(node.stateNode);
40 + }
41 + }
42 + let child = node.child;
43 +
44 + if (isFiberSuspenseAndTimedOut(node)) {
45 + child = getSuspenseFallbackChild(node);
46 + }
47 + if (child !== null) {
48 + collectScopedNodesFromChildren(child, fn, scopedNodes);
49 + }
50 +}
51 +
52 +function collectScopedNodesFromChildren(
53 + startingChild: Fiber,
54 + fn: (type: string | Object, props: Object) => boolean,
55 + scopedNodes: Array<any>,
56 +): void {
57 + let child = startingChild;
58 + while (child !== null) {
59 + collectScopedNodes(child, fn, scopedNodes);
60 + child = child.sibling;
61 + }
62 +}
63 +
64 +function collectNearestScopeMethods(
65 + node: Fiber,
66 + scope: ReactScope,
67 + childrenScopes: Array<ReactScopeMethods>,
68 +): void {
69 + if (node.tag === ScopeComponent && node.type === scope) {
70 + childrenScopes.push(node.stateNode.methods);
71 + } else {
72 + let child = node.child;
73 +
74 + if (isFiberSuspenseAndTimedOut(node)) {
75 + child = getSuspenseFallbackChild(node);
76 + }
77 + if (child !== null) {
78 + collectNearestChildScopeMethods(child, scope, childrenScopes);
79 + }
80 + }
81 +}
82 +
83 +function collectNearestChildScopeMethods(
84 + startingChild: Fiber,
85 + scope: ReactScope,
86 + childrenScopes: Array<ReactScopeMethods>,
87 +): void {
88 + let child = startingChild;
89 + while (child !== null) {
90 + collectNearestScopeMethods(child, scope, childrenScopes);
91 + child = child.sibling;
92 + }
93 +}
94 +
95 +export function createScopeMethods(
96 + scope: ReactScope,
97 + instance: ReactScopeInstance,
98 +): ReactScopeMethods {
99 + const fn = scope.fn;
100 + return {
101 + getChildren(): null | Array<ReactScopeMethods> {
102 + const currentFiber = ((instance.fiber: any): Fiber);
103 + const child = currentFiber.child;
104 + const childrenScopes = [];
105 + if (child !== null) {
106 + collectNearestChildScopeMethods(child, scope, childrenScopes);
107 + }
108 + return childrenScopes.length === 0 ? null : childrenScopes;
109 + },
110 + getParent(): null | ReactScopeMethods {
111 + let node = ((instance.fiber: any): Fiber).return;
112 + while (node !== null) {
113 + if (node.tag === ScopeComponent && node.type === scope) {
114 + return node.stateNode.methods;
115 + }
116 + node = node.return;
117 + }
118 + return null;
119 + },
120 + getScopedNodes(): null | Array<Object> {
121 + const currentFiber = ((instance.fiber: any): Fiber);
122 + const child = currentFiber.child;
123 + const scopedNodes = [];
124 + if (child !== null) {
125 + collectScopedNodesFromChildren(child, fn, scopedNodes);
126 + }
127 + return scopedNodes.length === 0 ? null : scopedNodes;
128 + },
129 + };
130 +}
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js new
+168
@@ -0,0 +1,168 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +let React;
13 +let ReactFeatureFlags;
14 +
15 +describe('ReactScope', () => {
16 + beforeEach(() => {
17 + jest.resetModules();
18 + ReactFeatureFlags = require('shared/ReactFeatureFlags');
19 + ReactFeatureFlags.enableScopeAPI = true;
20 + React = require('react');
21 + });
22 +
23 + describe('ReactDOM', () => {
24 + let ReactDOM;
25 + let container;
26 +
27 + beforeEach(() => {
28 + ReactDOM = require('react-dom');
29 + container = document.createElement('div');
30 + document.body.appendChild(container);
31 + });
32 +
33 + afterEach(() => {
34 + document.body.removeChild(container);
35 + container = null;
36 + });
37 +
38 + it('getScopedNodes() works as intended', () => {
39 + const TestScope = React.unstable_createScope((type, props) => true);
40 + const scopeRef = React.createRef();
41 + const divRef = React.createRef();
42 + const spanRef = React.createRef();
43 + const aRef = React.createRef();
44 +
45 + function Test({toggle}) {
46 + return toggle ? (
47 + <TestScope ref={scopeRef}>
48 + <div ref={divRef}>DIV</div>
49 + <span ref={spanRef}>SPAN</span>
50 + <a ref={aRef}>A</a>
51 + </TestScope>
52 + ) : (
53 + <TestScope ref={scopeRef}>
54 + <a ref={aRef}>A</a>
55 + <div ref={divRef}>DIV</div>
56 + <span ref={spanRef}>SPAN</span>
57 + </TestScope>
58 + );
59 + }
60 +
61 + ReactDOM.render(<Test toggle={true} />, container);
62 + let nodes = scopeRef.current.getScopedNodes();
63 + expect(nodes).toEqual([divRef.current, spanRef.current, aRef.current]);
64 + ReactDOM.render(<Test toggle={false} />, container);
65 + nodes = scopeRef.current.getScopedNodes();
66 + expect(nodes).toEqual([aRef.current, divRef.current, spanRef.current]);
67 + });
68 +
69 + it('mixed getParent() and getScopedNodes() works as intended', () => {
70 + const TestScope = React.unstable_createScope((type, props) => true);
71 + const TestScope2 = React.unstable_createScope((type, props) => true);
72 + const refA = React.createRef();
73 + const refB = React.createRef();
74 + const refC = React.createRef();
75 + const refD = React.createRef();
76 + const spanA = React.createRef();
77 + const spanB = React.createRef();
78 + const divA = React.createRef();
79 + const divB = React.createRef();
80 +
81 + function Test() {
82 + return (
83 + <div>
84 + <TestScope ref={refA}>
85 + <span ref={spanA}>
86 + <TestScope2 ref={refB}>
87 + <div ref={divA}>
88 + <TestScope ref={refC}>
89 + <span ref={spanB}>
90 + <TestScope2 ref={refD}>
91 + <div ref={divB}>>Hello world</div>
92 + </TestScope2>
93 + </span>
94 + </TestScope>
95 + </div>
96 + </TestScope2>
97 + </span>
98 + </TestScope>
99 + </div>
100 + );
101 + }
102 +
103 + ReactDOM.render(<Test />, container);
104 + const dParent = refD.current.getParent();
105 + expect(dParent).not.toBe(null);
106 + expect(dParent.getScopedNodes()).toEqual([
107 + divA.current,
108 + spanB.current,
109 + divB.current,
110 + ]);
111 + const cParent = refC.current.getParent();
112 + expect(cParent).not.toBe(null);
113 + expect(cParent.getScopedNodes()).toEqual([
114 + spanA.current,
115 + divA.current,
116 + spanB.current,
117 + divB.current,
118 + ]);
119 + expect(refB.current.getParent()).toBe(null);
120 + expect(refA.current.getParent()).toBe(null);
121 + });
122 +
123 + it('getChildren() works as intended', () => {
124 + const TestScope = React.unstable_createScope((type, props) => true);
125 + const TestScope2 = React.unstable_createScope((type, props) => true);
126 + const refA = React.createRef();
127 + const refB = React.createRef();
128 + const refC = React.createRef();
129 + const refD = React.createRef();
130 + const spanA = React.createRef();
131 + const spanB = React.createRef();
132 + const divA = React.createRef();
133 + const divB = React.createRef();
134 +
135 + function Test() {
136 + return (
137 + <div>
138 + <TestScope ref={refA}>
139 + <span ref={spanA}>
140 + <TestScope2 ref={refB}>
141 + <div ref={divA}>
142 + <TestScope ref={refC}>
143 + <span ref={spanB}>
144 + <TestScope2 ref={refD}>
145 + <div ref={divB}>>Hello world</div>
146 + </TestScope2>
147 + </span>
148 + </TestScope>
149 + </div>
150 + </TestScope2>
151 + </span>
152 + </TestScope>
153 + </div>
154 + );
155 + }
156 +
157 + ReactDOM.render(<Test />, container);
158 + const dChildren = refD.current.getChildren();
159 + expect(dChildren).toBe(null);
160 + const cChildren = refC.current.getChildren();
161 + expect(cChildren).toBe(null);
162 + const bChildren = refB.current.getChildren();
163 + expect(bChildren).toEqual([refD.current]);
164 + const aChildren = refA.current.getChildren();
165 + expect(aChildren).toEqual([refC.current]);
166 + });
167 + });
168 +});
packages/react/src/React.js
+6
@@ -53,10 +53,12 @@ import {
53 import ReactSharedInternals from './ReactSharedInternals';
54 import createFundamental from 'shared/createFundamentalComponent';
55 import createResponder from 'shared/createEventResponder';
56 +import createScope from 'shared/createScope';
57 import {
58 enableJSXTransformAPI,
59 enableFlareAPI,
60 enableFundamentalAPI,
61 + enableScopeAPI,
62 } from 'shared/ReactFeatureFlags';
63 const React = {
64 Children: {
@@ -114,6 +116,10 @@ if (enableFundamentalAPI) {
116 React.unstable_createFundamental = createFundamental;
117 }
118
119 +if (enableScopeAPI) {
120 + React.unstable_createScope = createScope;
121 +}
122 +
123 // Note: some APIs are added with feature flags.
124 // Make sure that stable builds for open source
125 // don't modify the React object to avoid deopts.
packages/shared/ReactDOMTypes.js
-1
@@ -66,7 +66,6 @@ export type ReactDOMResponderContext = {
66 removeRootEventTypes: (rootEventTypes: Array<string>) => void,
67 setTimeout: (func: () => void, timeout: number) => number,
68 clearTimeout: (timerId: number) => void,
69 - getFocusableElementsInScope(deep: boolean): Array<HTMLElement>,
69 getActiveDocument(): Document,
70 objectAssign: Function,
71 getTimeStamp: () => number,
packages/shared/ReactFeatureFlags.js
+3
@@ -65,6 +65,9 @@ export const enableFlareAPI = false;
65 // Experimental Host Component support.
66 export const enableFundamentalAPI = false;
67
68 +// Experimental Scope support.
69 +export const enableScopeAPI = false;
70 +
71 // New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107
72 export const enableJSXTransformAPI = false;
73
packages/shared/ReactSymbols.js
+1
@@ -57,6 +57,7 @@ export const REACT_FUNDAMENTAL_TYPE = hasSymbol
57 export const REACT_RESPONDER_TYPE = hasSymbol
58 ? Symbol.for('react.responder')
59 : 0xead6;
60 +export const REACT_SCOPE_TYPE = hasSymbol ? Symbol.for('react.scope') : 0xead7;
61
62 const MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
63 const FAUX_ITERATOR_SYMBOL = '@@iterator';
packages/shared/ReactTypes.js
+16
@@ -159,3 +159,19 @@ export type ReactFundamentalComponent<C, H> = {|
159 $$typeof: Symbol | number,
160 impl: ReactFundamentalImpl<C, H>,
161 |};
162 +
163 +export type ReactScope = {|
164 + $$typeof: Symbol | number,
165 + fn: (type: string | Object, props: Object) => boolean,
166 +|};
167 +
168 +export type ReactScopeMethods = {|
169 + getChildren(): null | Array<ReactScopeMethods>,
170 + getParent(): null | ReactScopeMethods,
171 + getScopedNodes(): null | Array<Object>,
172 +|};
173 +
174 +export type ReactScopeInstance = {|
175 + fiber: Object,
176 + methods: null | ReactScopeMethods,
177 +|};
packages/shared/ReactWorkTags.js
+3 -1
@@ -28,7 +28,8 @@ export type WorkTag =
28 | 17
29 | 18
30 | 19
31 - | 20;
31 + | 20
32 + | 21;
33
34 export const FunctionComponent = 0;
35 export const ClassComponent = 1;
@@ -51,3 +52,4 @@ export const IncompleteClassComponent = 17;
52 export const DehydratedFragment = 18;
53 export const SuspenseListComponent = 19;
54 export const FundamentalComponent = 20;
55 +export const ScopeComponent = 21;
packages/shared/createScope.js new
+23
@@ -0,0 +1,23 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + * @flow
7 + */
8 +
9 +import type {ReactScope} from 'shared/ReactTypes';
10 +import {REACT_SCOPE_TYPE} from 'shared/ReactSymbols';
11 +
12 +export default function createScope(
13 + fn: (type: string | Object, props: Object) => boolean,
14 +): ReactScope {
15 + const scopeComponent = {
16 + $$typeof: REACT_SCOPE_TYPE,
17 + fn,
18 + };
19 + if (__DEV__) {
20 + Object.freeze(scopeComponent);
21 + }
22 + return scopeComponent;
23 +}
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -33,6 +33,7 @@ export const warnAboutDeprecatedLifecycles = true;
33 export const warnAboutDeprecatedSetNativeProps = true;
34 export const enableFlareAPI = false;
35 export const enableFundamentalAPI = false;
36 +export const enableScopeAPI = false;
37 export const enableJSXTransformAPI = false;
38 export const warnAboutUnmockedScheduler = true;
39 export const flushSuspenseFallbacksInTests = true;
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -28,6 +28,7 @@ export const enableSchedulerDebugging = false;
28 export const warnAboutDeprecatedSetNativeProps = false;
29 export const enableFlareAPI = false;
30 export const enableFundamentalAPI = false;
31 +export const enableScopeAPI = false;
32 export const enableJSXTransformAPI = false;
33 export const warnAboutUnmockedScheduler = false;
34 export const flushSuspenseFallbacksInTests = true;
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -28,6 +28,7 @@ export const enableSchedulerDebugging = false;
28 export const warnAboutDeprecatedSetNativeProps = false;
29 export const enableFlareAPI = false;
30 export const enableFundamentalAPI = false;
31 +export const enableScopeAPI = false;
32 export const enableJSXTransformAPI = false;
33 export const warnAboutUnmockedScheduler = true;
34 export const flushSuspenseFallbacksInTests = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -28,6 +28,7 @@ export const enableSchedulerDebugging = false;
28 export const warnAboutDeprecatedSetNativeProps = false;
29 export const enableFlareAPI = false;
30 export const enableFundamentalAPI = false;
31 +export const enableScopeAPI = false;
32 export const enableJSXTransformAPI = false;
33 export const warnAboutUnmockedScheduler = false;
34 export const flushSuspenseFallbacksInTests = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -26,6 +26,7 @@ export const warnAboutDeprecatedSetNativeProps = false;
26 export const disableJavaScriptURLs = false;
27 export const enableFlareAPI = true;
28 export const enableFundamentalAPI = false;
29 +export const enableScopeAPI = false;
30 export const enableJSXTransformAPI = true;
31 export const warnAboutUnmockedScheduler = true;
32 export const flushSuspenseFallbacksInTests = true;
packages/shared/forks/ReactFeatureFlags.www.js
+2
@@ -73,6 +73,8 @@ export const enableFlareAPI = true;
73
74 export const enableFundamentalAPI = false;
75
76 +export const enableScopeAPI = false;
77 +
78 export const enableJSXTransformAPI = true;
79
80 export const warnAboutUnmockedScheduler = true;
packages/shared/isValidElementType.js
+3 -1
@@ -21,6 +21,7 @@ import {
21 REACT_LAZY_TYPE,
22 REACT_FUNDAMENTAL_TYPE,
23 REACT_RESPONDER_TYPE,
24 + REACT_SCOPE_TYPE,
25 } from 'shared/ReactSymbols';
26
27 export default function isValidElementType(type: mixed) {
@@ -42,6 +43,7 @@ export default function isValidElementType(type: mixed) {
43 type.$$typeof === REACT_CONTEXT_TYPE ||
44 type.$$typeof === REACT_FORWARD_REF_TYPE ||
45 type.$$typeof === REACT_FUNDAMENTAL_TYPE ||
45 - type.$$typeof === REACT_RESPONDER_TYPE))
46 + type.$$typeof === REACT_RESPONDER_TYPE ||
47 + type.$$typeof === REACT_SCOPE_TYPE))
48 );
49 }