Tidy up React Scope API (#19352)
Dominic Gannaway committed
Jul 16, 2020 at 16:21 UTC
9102719baacb64738e9235bc80d3d7d9918cc74c
14 files changed
+136
-172
packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js
+80
-85
@@ -2728,6 +2728,77 @@ describe('DOMModernPluginEventSystem', () => {
2728
document.body.removeChild(container2);
2729
});
2730
2731
+ // @gate experimental
2732
+ it('handle propagation of click events between disjointed comment roots', () => {
2733
+ const buttonRef = React.createRef();
2734
+ const divRef = React.createRef();
2735
+ const log = [];
2736
+ const setClick = ReactDOM.unstable_createEventHandle('click');
2737
+ const setClickCapture = ReactDOM.unstable_createEventHandle(
2738
+ 'click',
2739
+ {capture: true},
2740
+ );
2741
+ const onClick = jest.fn(e => log.push(['bubble', e.currentTarget]));
2742
+ const onClickCapture = jest.fn(e =>
2743
+ log.push(['capture', e.currentTarget]),
2744
+ );
2745
+
2746
+ function Child() {
2747
+ React.useEffect(() => {
2748
+ const click1 = setClick(divRef.current, onClick);
2749
+ const click2 = setClickCapture(divRef.current, onClickCapture);
2750
+ return () => {
2751
+ click1();
2752
+ click2();
2753
+ };
2754
+ });
2755
+
2756
+ return <div ref={divRef}>Click me!</div>;
2757
+ }
2758
+
2759
+ function Parent() {
2760
+ React.useEffect(() => {
2761
+ const click1 = setClick(buttonRef.current, onClick);
2762
+ const click2 = setClickCapture(
2763
+ buttonRef.current,
2764
+ onClickCapture,
2765
+ );
2766
+ return () => {
2767
+ click1();
2768
+ click2();
2769
+ };
2770
+ });
2771
+
2772
+ return <button ref={buttonRef} />;
2773
+ }
2774
+
2775
+ // We use a comment node here, then mount to it
2776
+ const disjointedNode = document.createComment(
2777
+ ' react-mount-point-unstable ',
2778
+ );
2779
+ ReactDOM.render(<Parent />, container);
2780
+ Scheduler.unstable_flushAll();
2781
+ buttonRef.current.appendChild(disjointedNode);
2782
+ ReactDOM.render(<Child />, disjointedNode);
2783
+ Scheduler.unstable_flushAll();
2784
+
2785
+ const buttonElement = buttonRef.current;
2786
+ dispatchClickEvent(buttonElement);
2787
+ expect(onClick).toHaveBeenCalledTimes(1);
2788
+ expect(onClickCapture).toHaveBeenCalledTimes(1);
2789
+ expect(log[0]).toEqual(['capture', buttonElement]);
2790
+ expect(log[1]).toEqual(['bubble', buttonElement]);
2791
+
2792
+ const divElement = divRef.current;
2793
+ dispatchClickEvent(divElement);
2794
+ expect(onClick).toHaveBeenCalledTimes(3);
2795
+ expect(onClickCapture).toHaveBeenCalledTimes(3);
2796
+ expect(log[2]).toEqual(['capture', buttonElement]);
2797
+ expect(log[3]).toEqual(['capture', divElement]);
2798
+ expect(log[4]).toEqual(['bubble', divElement]);
2799
+ expect(log[5]).toEqual(['bubble', buttonElement]);
2800
+ });
2801
+
2802
describe('Compatibility with Scopes API', () => {
2803
beforeEach(() => {
2804
jest.resetModules();
@@ -2751,7 +2822,7 @@ describe('DOMModernPluginEventSystem', () => {
2822
const onClickCapture = jest.fn(e =>
2823
log.push(['capture', e.currentTarget]),
2824
);
2754
- const TestScope = React.unstable_createScope();
2825
+ const TestScope = React.unstable_Scope;
2826
const setClick = ReactDOM.unstable_createEventHandle('click');
2827
const setClickCapture = ReactDOM.unstable_createEventHandle(
2828
'click',
@@ -2808,7 +2879,7 @@ describe('DOMModernPluginEventSystem', () => {
2879
const onClickCapture = jest.fn(e =>
2880
log.push(['capture', e.currentTarget]),
2881
);
2811
- const TestScope = React.unstable_createScope();
2882
+ const TestScope = React.unstable_Scope;
2883
const setClick = ReactDOM.unstable_createEventHandle('click');
2884
const setClickCapture = ReactDOM.unstable_createEventHandle(
2885
'click',
@@ -2892,7 +2963,7 @@ describe('DOMModernPluginEventSystem', () => {
2963
it('should not handle the target being a dangling text node within a scope', () => {
2964
const clickEvent = jest.fn();
2965
const buttonRef = React.createRef();
2895
- const TestScope = React.unstable_createScope();
2966
+ const TestScope = React.unstable_Scope;
2967
const setClick = ReactDOM.unstable_createEventHandle('click');
2968
2969
function Test() {
@@ -2924,8 +2995,8 @@ describe('DOMModernPluginEventSystem', () => {
2995
const buttonRef = React.createRef();
2996
const outerOnClick = jest.fn();
2997
const innerOnClick = jest.fn(e => e.stopPropagation());
2927
- const TestScope = React.unstable_createScope();
2928
- const TestScope2 = React.unstable_createScope();
2998
+ const TestScope = React.unstable_Scope;
2999
+ const TestScope2 = React.unstable_Scope;
3000
const setClick = ReactDOM.unstable_createEventHandle('click');
3001
3002
function Test() {
@@ -2966,8 +3037,8 @@ describe('DOMModernPluginEventSystem', () => {
3037
const buttonRef = React.createRef();
3038
const outerOnClick = jest.fn(e => e.stopPropagation());
3039
const innerOnClick = jest.fn();
2969
- const TestScope = React.unstable_createScope();
2970
- const TestScope2 = React.unstable_createScope();
3040
+ const TestScope = React.unstable_Scope;
3041
+ const TestScope2 = React.unstable_Scope;
3042
const setClick = ReactDOM.unstable_createEventHandle('click');
3043
3044
function Test() {
@@ -3007,8 +3078,8 @@ describe('DOMModernPluginEventSystem', () => {
3078
it('handle stopPropagation (inner and outer) correctly between scopes', () => {
3079
const buttonRef = React.createRef();
3080
const onClick = jest.fn(e => e.stopPropagation());
3010
- const TestScope = React.unstable_createScope();
3011
- const TestScope2 = React.unstable_createScope();
3081
+ const TestScope = React.unstable_Scope;
3082
+ const TestScope2 = React.unstable_Scope;
3083
const setClick = ReactDOM.unstable_createEventHandle('click');
3084
3085
function Test() {
@@ -3042,82 +3113,6 @@ describe('DOMModernPluginEventSystem', () => {
3113
3114
expect(onClick).toHaveBeenCalledTimes(1);
3115
});
3045
-
3046
- // @gate experimental
3047
- it('handle propagation of click events between disjointed comment roots', () => {
3048
- const buttonRef = React.createRef();
3049
- const divRef = React.createRef();
3050
- const log = [];
3051
- const setClick = ReactDOM.unstable_createEventHandle('click');
3052
- const setClickCapture = ReactDOM.unstable_createEventHandle(
3053
- 'click',
3054
- {capture: true},
3055
- );
3056
- const onClick = jest.fn(e =>
3057
- log.push(['bubble', e.currentTarget]),
3058
- );
3059
- const onClickCapture = jest.fn(e =>
3060
- log.push(['capture', e.currentTarget]),
3061
- );
3062
-
3063
- function Child() {
3064
- React.useEffect(() => {
3065
- const click1 = setClick(divRef.current, onClick);
3066
- const click2 = setClickCapture(
3067
- divRef.current,
3068
- onClickCapture,
3069
- );
3070
- return () => {
3071
- click1();
3072
- click2();
3073
- };
3074
- });
3075
-
3076
- return <div ref={divRef}>Click me!</div>;
3077
- }
3078
-
3079
- function Parent() {
3080
- React.useEffect(() => {
3081
- const click1 = setClick(buttonRef.current, onClick);
3082
- const click2 = setClickCapture(
3083
- buttonRef.current,
3084
- onClickCapture,
3085
- );
3086
- return () => {
3087
- click1();
3088
- click2();
3089
- };
3090
- });
3091
-
3092
- return <button ref={buttonRef} />;
3093
- }
3094
-
3095
- // We use a comment node here, then mount to it
3096
- const disjointedNode = document.createComment(
3097
- ' react-mount-point-unstable ',
3098
- );
3099
- ReactDOM.render(<Parent />, container);
3100
- Scheduler.unstable_flushAll();
3101
- buttonRef.current.appendChild(disjointedNode);
3102
- ReactDOM.render(<Child />, disjointedNode);
3103
- Scheduler.unstable_flushAll();
3104
-
3105
- const buttonElement = buttonRef.current;
3106
- dispatchClickEvent(buttonElement);
3107
- expect(onClick).toHaveBeenCalledTimes(1);
3108
- expect(onClickCapture).toHaveBeenCalledTimes(1);
3109
- expect(log[0]).toEqual(['capture', buttonElement]);
3110
- expect(log[1]).toEqual(['bubble', buttonElement]);
3111
-
3112
- const divElement = divRef.current;
3113
- dispatchClickEvent(divElement);
3114
- expect(onClick).toHaveBeenCalledTimes(3);
3115
- expect(onClickCapture).toHaveBeenCalledTimes(3);
3116
- expect(log[2]).toEqual(['capture', buttonElement]);
3117
- expect(log[3]).toEqual(['capture', divElement]);
3118
- expect(log[4]).toEqual(['bubble', divElement]);
3119
- expect(log[5]).toEqual(['bubble', buttonElement]);
3120
- });
3116
});
3117
});
3118
},
packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js
+1
-1
@@ -561,7 +561,7 @@ describe('DOMEventResponderSystem', () => {
561
it('the event responder onUnmount() function should fire using scopes', () => {
562
let onUnmountFired = 0;
563
564
- const TestScope = React.unstable_createScope();
564
+ const TestScope = React.unstable_Scope;
565
const TestResponder = createEventResponder({
566
targetEventTypes: [],
567
onUnmount: () => {
packages/react-dom/src/server/ReactPartialRenderer.js
+25
-25
@@ -1109,6 +1109,31 @@ class ReactDOMServerRenderer {
1109
}
1110
}
1111
// eslint-disable-next-line-no-fallthrough
1112
+ case REACT_SCOPE_TYPE: {
1113
+ if (enableScopeAPI) {
1114
+ const nextChildren = toArray(
1115
+ ((nextChild: any): ReactElement).props.children,
1116
+ );
1117
+ const frame: Frame = {
1118
+ type: null,
1119
+ domNamespace: parentNamespace,
1120
+ children: nextChildren,
1121
+ childIndex: 0,
1122
+ context: context,
1123
+ footer: '',
1124
+ };
1125
+ if (__DEV__) {
1126
+ ((frame: any): FrameDev).debugElementStack = [];
1127
+ }
1128
+ this.stack.push(frame);
1129
+ return '';
1130
+ }
1131
+ invariant(
1132
+ false,
1133
+ 'ReactDOMServer does not yet support scope components.',
1134
+ );
1135
+ }
1136
+ // eslint-disable-next-line-no-fallthrough
1137
default:
1138
break;
1139
}
@@ -1299,31 +1324,6 @@ class ReactDOMServerRenderer {
1324
this.stack.push(frame);
1325
return '';
1326
}
1302
- // eslint-disable-next-line-no-fallthrough
1303
- case REACT_SCOPE_TYPE: {
1304
- if (enableScopeAPI) {
1305
- const nextChildren = toArray(
1306
- ((nextChild: any): ReactElement).props.children,
1307
- );
1308
- const frame: Frame = {
1309
- type: null,
1310
- domNamespace: parentNamespace,
1311
- children: nextChildren,
1312
- childIndex: 0,
1313
- context: context,
1314
- footer: '',
1315
- };
1316
- if (__DEV__) {
1317
- ((frame: any): FrameDev).debugElementStack = [];
1318
- }
1319
- this.stack.push(frame);
1320
- return '';
1321
- }
1322
- invariant(
1323
- false,
1324
- 'ReactDOMServer does not yet support scope components.',
1325
- );
1326
- }
1327
}
1328
}
1329
packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js
+1
-1
@@ -409,7 +409,7 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
409
410
// @gate experimental
411
it('is called after a nested focused element is unmounted (with scope query)', () => {
412
- const TestScope = React.unstable_createScope();
412
+ const TestScope = React.unstable_Scope;
413
const testScopeQuery = (type, props) => true;
414
let targetNodes;
415
let targetNode;
packages/react-interactions/events/src/dom/create-event-handle/__tests__/useFocusWithin-test.internal.js
+1
-1
@@ -415,7 +415,7 @@ describe.each(table)(`useFocus`, hasPointerEvents => {
415
416
// @gate experimental
417
it('is called after a nested focused element is unmounted (with scope query)', () => {
418
- const TestScope = React.unstable_createScope();
418
+ const TestScope = React.unstable_Scope;
419
const testScopeQuery = (type, props) => true;
420
let targetNodes;
421
let targetNode;
packages/react-reconciler/src/ReactFiber.new.js
+5
-11
@@ -501,7 +501,11 @@ export function createFiberFromTypeAndProps(
501
return createFiberFromOffscreen(pendingProps, mode, lanes, key);
502
case REACT_LEGACY_HIDDEN_TYPE:
503
return createFiberFromLegacyHidden(pendingProps, mode, lanes, key);
504
-
504
+ case REACT_SCOPE_TYPE:
505
+ if (enableScopeAPI) {
506
+ return createFiberFromScope(type, pendingProps, mode, lanes, key);
507
+ }
508
+ // eslint-disable-next-line no-fallthrough
509
default: {
510
if (typeof type === 'object' && type !== null) {
511
switch (type.$$typeof) {
@@ -539,16 +543,6 @@ export function createFiberFromTypeAndProps(
543
);
544
}
545
break;
542
- case REACT_SCOPE_TYPE:
543
- if (enableScopeAPI) {
544
- return createFiberFromScope(
545
- type,
546
- pendingProps,
547
- mode,
548
- lanes,
549
- key,
550
- );
551
- }
546
}
547
}
548
let info = '';
packages/react-reconciler/src/ReactFiber.old.js
+5
-11
@@ -496,7 +496,11 @@ export function createFiberFromTypeAndProps(
496
return createFiberFromOffscreen(pendingProps, mode, lanes, key);
497
case REACT_LEGACY_HIDDEN_TYPE:
498
return createFiberFromLegacyHidden(pendingProps, mode, lanes, key);
499
-
499
+ case REACT_SCOPE_TYPE:
500
+ if (enableScopeAPI) {
501
+ return createFiberFromScope(type, pendingProps, mode, lanes, key);
502
+ }
503
+ // eslint-disable-next-line no-fallthrough
504
default: {
505
if (typeof type === 'object' && type !== null) {
506
switch (type.$$typeof) {
@@ -534,16 +538,6 @@ export function createFiberFromTypeAndProps(
538
);
539
}
540
break;
537
- case REACT_SCOPE_TYPE:
538
- if (enableScopeAPI) {
539
- return createFiberFromScope(
540
- type,
541
- pendingProps,
542
- mode,
543
- lanes,
544
- key,
545
- );
546
- }
541
}
542
}
543
let info = '';
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js
+10
-10
@@ -43,7 +43,7 @@ describe('ReactScope', () => {
43
// @gate experimental
44
it('DO_NOT_USE_queryAllNodes() works as intended', () => {
45
const testScopeQuery = (type, props) => true;
46
- const TestScope = React.unstable_createScope();
46
+ const TestScope = React.unstable_Scope;
47
const scopeRef = React.createRef();
48
const divRef = React.createRef();
49
const spanRef = React.createRef();
@@ -78,7 +78,7 @@ describe('ReactScope', () => {
78
// @gate experimental
79
it('DO_NOT_USE_queryAllNodes() provides the correct host instance', () => {
80
const testScopeQuery = (type, props) => type === 'div';
81
- const TestScope = React.unstable_createScope();
81
+ const TestScope = React.unstable_Scope;
82
const scopeRef = React.createRef();
83
const divRef = React.createRef();
84
const spanRef = React.createRef();
@@ -125,7 +125,7 @@ describe('ReactScope', () => {
125
// @gate experimental
126
it('DO_NOT_USE_queryFirstNode() works as intended', () => {
127
const testScopeQuery = (type, props) => true;
128
- const TestScope = React.unstable_createScope();
128
+ const TestScope = React.unstable_Scope;
129
const scopeRef = React.createRef();
130
const divRef = React.createRef();
131
const spanRef = React.createRef();
@@ -159,7 +159,7 @@ describe('ReactScope', () => {
159
160
// @gate experimental
161
it('containsNode() works as intended', () => {
162
- const TestScope = React.unstable_createScope();
162
+ const TestScope = React.unstable_Scope;
163
const scopeRef = React.createRef();
164
const divRef = React.createRef();
165
const spanRef = React.createRef();
@@ -209,7 +209,7 @@ describe('ReactScope', () => {
209
210
// @gate experimental
211
it('scopes support server-side rendering and hydration', () => {
212
- const TestScope = React.unstable_createScope();
212
+ const TestScope = React.unstable_Scope;
213
const scopeRef = React.createRef();
214
const divRef = React.createRef();
215
const spanRef = React.createRef();
@@ -241,7 +241,7 @@ describe('ReactScope', () => {
241
// @gate experimental
242
it('getChildContextValues() works as intended', () => {
243
const TestContext = React.createContext();
244
- const TestScope = React.unstable_createScope();
244
+ const TestScope = React.unstable_Scope;
245
const scopeRef = React.createRef();
246
247
function Test({toggle}) {
@@ -273,7 +273,7 @@ describe('ReactScope', () => {
273
let resolve;
274
const promise = new Promise(resolvePromise => (resolve = resolvePromise));
275
const ref = React.createRef();
276
- const TestScope = React.unstable_createScope();
276
+ const TestScope = React.unstable_Scope;
277
const scopeRef = React.createRef();
278
const testScopeQuery = (type, props) => true;
279
@@ -345,7 +345,7 @@ describe('ReactScope', () => {
345
// @gate experimental
346
it('DO_NOT_USE_queryAllNodes() works as intended', () => {
347
const testScopeQuery = (type, props) => true;
348
- const TestScope = React.unstable_createScope();
348
+ const TestScope = React.unstable_Scope;
349
const scopeRef = React.createRef();
350
const divRef = React.createRef();
351
const spanRef = React.createRef();
@@ -382,7 +382,7 @@ describe('ReactScope', () => {
382
// @gate experimental
383
it('DO_NOT_USE_queryFirstNode() works as intended', () => {
384
const testScopeQuery = (type, props) => true;
385
- const TestScope = React.unstable_createScope();
385
+ const TestScope = React.unstable_Scope;
386
const scopeRef = React.createRef();
387
const divRef = React.createRef();
388
const spanRef = React.createRef();
@@ -418,7 +418,7 @@ describe('ReactScope', () => {
418
419
// @gate experimental
420
it('containsNode() works as intended', () => {
421
- const TestScope = React.unstable_createScope();
421
+ const TestScope = React.unstable_Scope;
422
const scopeRef = React.createRef();
423
const divRef = React.createRef();
424
const spanRef = React.createRef();
packages/react/index.classic.fb.js
+1
-1
@@ -54,7 +54,7 @@ export {
54
DEPRECATED_useResponder,
55
DEPRECATED_createResponder,
56
// enableScopeAPI
57
- unstable_createScope,
57
+ unstable_Scope,
58
unstable_useOpaqueIdentifier,
59
// enableDebugTracing
60
unstable_DebugTracingMode,
packages/react/index.js
+1
-1
@@ -81,6 +81,6 @@ export {
81
DEPRECATED_useResponder,
82
DEPRECATED_createResponder,
83
unstable_createFundamental,
84
- unstable_createScope,
84
+ unstable_Scope,
85
unstable_useOpaqueIdentifier,
86
} from './src/React';
packages/react/index.modern.fb.js
+1
-1
@@ -53,7 +53,7 @@ export {
53
DEPRECATED_useResponder,
54
DEPRECATED_createResponder,
55
// enableScopeAPI
56
- unstable_createScope,
56
+ unstable_Scope,
57
unstable_useOpaqueIdentifier,
58
// enableDebugTracing
59
unstable_DebugTracingMode,
packages/react/src/React.js
+2
-2
@@ -16,6 +16,7 @@ import {
16
REACT_SUSPENSE_TYPE,
17
REACT_SUSPENSE_LIST_TYPE,
18
REACT_LEGACY_HIDDEN_TYPE,
19
+ REACT_SCOPE_TYPE,
20
} from 'shared/ReactSymbols';
21
22
import {Component, PureComponent} from './ReactBaseClasses';
@@ -59,7 +60,6 @@ import {createMutableSource} from './ReactMutableSource';
60
import ReactSharedInternals from './ReactSharedInternals';
61
import {createFundamental} from './ReactFundamental';
62
import {createEventResponder} from './ReactEventResponder';
62
-import {createScope} from './ReactScope';
63
64
// TODO: Move this branching into the other module instead and just re-export.
65
const createElement = __DEV__ ? createElementWithValidation : createElementProd;
@@ -121,6 +121,6 @@ export {
121
// enableFundamentalAPI
122
createFundamental as unstable_createFundamental,
123
// enableScopeAPI
124
- createScope as unstable_createScope,
124
+ REACT_SCOPE_TYPE as unstable_Scope,
125
useOpaqueIdentifier as unstable_useOpaqueIdentifier,
126
};
packages/react/src/ReactScope.js
deleted
-20
@@ -1,20 +0,0 @@
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 function createScope(): ReactScope {
13
- const scopeComponent = {
14
- $$typeof: REACT_SCOPE_TYPE,
15
- };
16
- if (__DEV__) {
17
- Object.freeze(scopeComponent);
18
- }
19
- return scopeComponent;
20
-}
packages/shared/isValidElementType.js
+3
-2
@@ -26,6 +26,7 @@ import {
26
REACT_SERVER_BLOCK_TYPE,
27
REACT_LEGACY_HIDDEN_TYPE,
28
} from 'shared/ReactSymbols';
29
+import {enableScopeAPI} from './ReactFeatureFlags';
30
31
export default function isValidElementType(type: mixed) {
32
if (typeof type === 'string' || typeof type === 'function') {
@@ -40,7 +41,8 @@ export default function isValidElementType(type: mixed) {
41
type === REACT_STRICT_MODE_TYPE ||
42
type === REACT_SUSPENSE_TYPE ||
43
type === REACT_SUSPENSE_LIST_TYPE ||
43
- type === REACT_LEGACY_HIDDEN_TYPE
44
+ type === REACT_LEGACY_HIDDEN_TYPE ||
45
+ (enableScopeAPI && type === REACT_SCOPE_TYPE)
46
) {
47
return true;
48
}
@@ -54,7 +56,6 @@ export default function isValidElementType(type: mixed) {
56
type.$$typeof === REACT_FORWARD_REF_TYPE ||
57
type.$$typeof === REACT_FUNDAMENTAL_TYPE ||
58
type.$$typeof === REACT_RESPONDER_TYPE ||
57
- type.$$typeof === REACT_SCOPE_TYPE ||
59
type.$$typeof === REACT_BLOCK_TYPE ||
60
type[(0: any)] === REACT_SERVER_BLOCK_TYPE
61
) {