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

Adds experimental fundamental interface (#16049)

Dominic Gannaway committed Jul 19, 2019 at 22:20 UTC 2c4d61e1022ae383dd11fe237f6df8451e6f0310
35 files changed +898 -40
package.json
+1
@@ -104,6 +104,7 @@
104 "debug-test": "cross-env NODE_ENV=development node --inspect-brk node_modules/.bin/jest --config ./scripts/jest/config.source.js --runInBand",
105 "test": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source.js",
106 "test-persistent": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-persistent.js",
107 + "debug-test-persistent": "cross-env NODE_ENV=development node --inspect-brk node_modules/.bin/jest --config ./scripts/jest/config.source-persistent.js --runInBand",
108 "test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js",
109 "test-prod-build": "yarn test-build-prod",
110 "test-build": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.build.js",
packages/react-art/src/ReactARTHostConfig.js
+20
@@ -444,3 +444,23 @@ export function unmountEventComponent(
444 ): void {
445 throw new Error('Not yet implemented.');
446 }
447 +
448 +export function getFundamentalComponentInstance(fundamentalInstance) {
449 + throw new Error('Not yet implemented.');
450 +}
451 +
452 +export function mountFundamentalComponent(fundamentalInstance) {
453 + throw new Error('Not yet implemented.');
454 +}
455 +
456 +export function shouldUpdateFundamentalComponent(fundamentalInstance) {
457 + throw new Error('Not yet implemented.');
458 +}
459 +
460 +export function updateFundamentalComponent(fundamentalInstance) {
461 + throw new Error('Not yet implemented.');
462 +}
463 +
464 +export function unmountFundamentalComponent(fundamentalInstance) {
465 + throw new Error('Not yet implemented.');
466 +}
packages/react-dom/src/client/ReactDOMHostConfig.js
+64
@@ -45,6 +45,7 @@ import type {DOMContainer} from './ReactDOM';
45 import type {
46 ReactDOMEventResponder,
47 ReactDOMEventComponentInstance,
48 + ReactDOMFundamentalComponentInstance,
49 } from 'shared/ReactDOMTypes';
50 import {
51 addRootEventTypesForComponentInstance,
@@ -103,6 +104,7 @@ export type NoTimeout = -1;
104 import {
105 enableSuspenseServerRenderer,
106 enableFlareAPI,
107 + enableFundamentalAPI,
108 } from 'shared/ReactFeatureFlags';
109 import warning from 'shared/warning';
110
@@ -882,3 +884,65 @@ export function unmountEventComponent(
884 unmountEventResponder(eventComponentInstance);
885 }
886 }
887 +
888 +export function getFundamentalComponentInstance(
889 + fundamentalInstance: ReactDOMFundamentalComponentInstance,
890 +): Instance {
891 + if (enableFundamentalAPI) {
892 + const {currentFiber, impl, props, state} = fundamentalInstance;
893 + const instance = impl.getInstance(null, props, state);
894 + precacheFiberNode(currentFiber, instance);
895 + return instance;
896 + }
897 + // Because of the flag above, this gets around the Flow error;
898 + return (null: any);
899 +}
900 +
901 +export function mountFundamentalComponent(
902 + fundamentalInstance: ReactDOMFundamentalComponentInstance,
903 +): void {
904 + if (enableFundamentalAPI) {
905 + const {impl, instance, props, state} = fundamentalInstance;
906 + const onMount = impl.onMount;
907 + if (onMount !== undefined) {
908 + onMount(null, instance, props, state);
909 + }
910 + }
911 +}
912 +
913 +export function shouldUpdateFundamentalComponent(
914 + fundamentalInstance: ReactDOMFundamentalComponentInstance,
915 +): boolean {
916 + if (enableFundamentalAPI) {
917 + const {impl, prevProps, props, state} = fundamentalInstance;
918 + const shouldUpdate = impl.shouldUpdate;
919 + if (shouldUpdate !== undefined) {
920 + return shouldUpdate(null, prevProps, props, state);
921 + }
922 + }
923 + return true;
924 +}
925 +
926 +export function updateFundamentalComponent(
927 + fundamentalInstance: ReactDOMFundamentalComponentInstance,
928 +): void {
929 + if (enableFundamentalAPI) {
930 + const {impl, instance, prevProps, props, state} = fundamentalInstance;
931 + const onUpdate = impl.onUpdate;
932 + if (onUpdate !== undefined) {
933 + onUpdate(null, instance, prevProps, props, state);
934 + }
935 + }
936 +}
937 +
938 +export function unmountFundamentalComponent(
939 + fundamentalInstance: ReactDOMFundamentalComponentInstance,
940 +): void {
941 + if (enableFundamentalAPI) {
942 + const {impl, instance, props, state} = fundamentalInstance;
943 + const onUnmount = impl.onUnmount;
944 + if (onUnmount !== undefined) {
945 + onUnmount(null, instance, props, state);
946 + }
947 + }
948 +}
packages/react-dom/src/server/ReactPartialRenderer.js
+39
@@ -23,6 +23,7 @@ import {
23 warnAboutDeprecatedLifecycles,
24 enableSuspenseServerRenderer,
25 enableFlareAPI,
26 + enableFundamentalAPI,
27 } from 'shared/ReactFeatureFlags';
28
29 import {
@@ -39,6 +40,7 @@ import {
40 REACT_LAZY_TYPE,
41 REACT_MEMO_TYPE,
42 REACT_EVENT_COMPONENT_TYPE,
43 + REACT_FUNDAMENTAL_TYPE,
44 } from 'shared/ReactSymbols';
45
46 import {
@@ -1190,6 +1192,43 @@ class ReactDOMServerRenderer {
1192 );
1193 }
1194 // eslint-disable-next-line-no-fallthrough
1195 + case REACT_FUNDAMENTAL_TYPE: {
1196 + if (enableFundamentalAPI) {
1197 + const fundamentalImpl = elementType.impl;
1198 + const open = fundamentalImpl.getServerSideString(
1199 + null,
1200 + nextElement.props,
1201 + );
1202 + const getServerSideStringClose =
1203 + fundamentalImpl.getServerSideStringClose;
1204 + const close =
1205 + getServerSideStringClose !== undefined
1206 + ? getServerSideStringClose(null, nextElement.props)
1207 + : '';
1208 + const nextChildren =
1209 + fundamentalImpl.reconcileChildren !== false
1210 + ? toArray(((nextChild: any): ReactElement).props.children)
1211 + : [];
1212 + const frame: Frame = {
1213 + type: null,
1214 + domNamespace: parentNamespace,
1215 + children: nextChildren,
1216 + childIndex: 0,
1217 + context: context,
1218 + footer: close,
1219 + };
1220 + if (__DEV__) {
1221 + ((frame: any): FrameDev).debugElementStack = [];
1222 + }
1223 + this.stack.push(frame);
1224 + return open;
1225 + }
1226 + invariant(
1227 + false,
1228 + 'ReactDOMServer does not yet support the fundamental API.',
1229 + );
1230 + }
1231 + // eslint-disable-next-line-no-fallthrough
1232 case REACT_LAZY_TYPE:
1233 invariant(
1234 false,
packages/react-native-renderer/src/ReactFabricHostConfig.js
+24
@@ -472,3 +472,27 @@ export function unmountEventComponent(
472 unmountEventResponder(eventComponentInstance);
473 }
474 }
475 +
476 +export function getFundamentalComponentInstance(fundamentalInstance) {
477 + throw new Error('Not yet implemented.');
478 +}
479 +
480 +export function mountFundamentalComponent(fundamentalInstance) {
481 + throw new Error('Not yet implemented.');
482 +}
483 +
484 +export function shouldUpdateFundamentalComponent(fundamentalInstance) {
485 + throw new Error('Not yet implemented.');
486 +}
487 +
488 +export function updateFundamentalComponent(fundamentalInstance) {
489 + throw new Error('Not yet implemented.');
490 +}
491 +
492 +export function unmountFundamentalComponent(fundamentalInstance) {
493 + throw new Error('Not yet implemented.');
494 +}
495 +
496 +export function cloneFundamentalInstance(fundamentalInstance) {
497 + throw new Error('Not yet implemented.');
498 +}
packages/react-native-renderer/src/ReactNativeHostConfig.js
+20
@@ -511,3 +511,23 @@ export function unmountEventComponent(
511 ): void {
512 throw new Error('Not yet implemented.');
513 }
514 +
515 +export function getFundamentalComponentInstance(fundamentalInstance) {
516 + throw new Error('Not yet implemented.');
517 +}
518 +
519 +export function mountFundamentalComponent(fundamentalInstance) {
520 + throw new Error('Not yet implemented.');
521 +}
522 +
523 +export function shouldUpdateFundamentalComponent(fundamentalInstance) {
524 + throw new Error('Not yet implemented.');
525 +}
526 +
527 +export function updateFundamentalComponent(fundamentalInstance) {
528 + throw new Error('Not yet implemented.');
529 +}
530 +
531 +export function unmountFundamentalComponent(fundamentalInstance) {
532 + throw new Error('Not yet implemented.');
533 +}
packages/react-noop-renderer/src/createReactNoop.js
+51
@@ -407,6 +407,57 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
407 unmountEventComponent(): void {
408 // NO-OP
409 },
410 +
411 + getFundamentalComponentInstance(fundamentalInstance): Instance {
412 + const {impl, props, state} = fundamentalInstance;
413 + return impl.getInstance(null, props, state);
414 + },
415 +
416 + mountFundamentalComponent(fundamentalInstance): void {
417 + const {impl, instance, props, state} = fundamentalInstance;
418 + const onMount = impl.onUpdate;
419 + if (onMount !== undefined) {
420 + onMount(null, instance, props, state);
421 + }
422 + },
423 +
424 + shouldUpdateFundamentalComponent(fundamentalInstance): boolean {
425 + const {impl, instance, prevProps, props, state} = fundamentalInstance;
426 + const shouldUpdate = impl.shouldUpdate;
427 + if (shouldUpdate !== undefined) {
428 + return shouldUpdate(null, instance, prevProps, props, state);
429 + }
430 + return true;
431 + },
432 +
433 + updateFundamentalComponent(fundamentalInstance): void {
434 + const {impl, instance, prevProps, props, state} = fundamentalInstance;
435 + const onUpdate = impl.onUpdate;
436 + if (onUpdate !== undefined) {
437 + onUpdate(null, instance, prevProps, props, state);
438 + }
439 + },
440 +
441 + unmountFundamentalComponent(fundamentalInstance): void {
442 + const {impl, instance, props, state} = fundamentalInstance;
443 + const onUnmount = impl.onUnmount;
444 + if (onUnmount !== undefined) {
445 + onUnmount(null, instance, props, state);
446 + }
447 + },
448 +
449 + cloneFundamentalInstance(fundamentalInstance): Instance {
450 + const instance = fundamentalInstance.instance;
451 + return {
452 + children: [],
453 + text: instance.text,
454 + type: instance.type,
455 + prop: instance.prop,
456 + id: instance.id,
457 + context: instance.context,
458 + hidden: instance.hidden,
459 + };
460 + },
461 };
462
463 const hostConfig = useMutation
packages/react-reconciler/src/ReactFiber.js
+34 -2
@@ -13,6 +13,7 @@ import type {
13 ReactPortal,
14 RefObject,
15 ReactEventComponent,
16 + ReactFundamentalComponent,
17 } from 'shared/ReactTypes';
18 import type {RootTag} from 'shared/ReactRootTags';
19 import type {WorkTag} from 'shared/ReactWorkTags';
@@ -26,7 +27,11 @@ import type {ReactEventComponentInstance} from 'shared/ReactTypes';
27
28 import invariant from 'shared/invariant';
29 import warningWithoutStack from 'shared/warningWithoutStack';
29 -import {enableProfilerTimer, enableFlareAPI} from 'shared/ReactFeatureFlags';
30 +import {
31 + enableProfilerTimer,
32 + enableFlareAPI,
33 + enableFundamentalAPI,
34 +} from 'shared/ReactFeatureFlags';
35 import {NoEffect, Placement} from 'shared/ReactSideEffectTags';
36 import {ConcurrentRoot, BatchedRoot} from 'shared/ReactRootTags';
37 import {
@@ -49,6 +54,7 @@ import {
54 SimpleMemoComponent,
55 LazyComponent,
56 EventComponent,
57 + FundamentalComponent,
58 } from 'shared/ReactWorkTags';
59 import getComponentName from 'shared/getComponentName';
60
@@ -79,6 +85,7 @@ import {
85 REACT_MEMO_TYPE,
86 REACT_LAZY_TYPE,
87 REACT_EVENT_COMPONENT_TYPE,
88 + REACT_FUNDAMENTAL_TYPE,
89 } from 'shared/ReactSymbols';
90
91 let hasBadMapPolyfill;
@@ -663,6 +670,17 @@ export function createFiberFromTypeAndProps(
670 );
671 }
672 break;
673 + case REACT_FUNDAMENTAL_TYPE:
674 + if (enableFundamentalAPI) {
675 + return createFiberFromFundamental(
676 + type,
677 + pendingProps,
678 + mode,
679 + expirationTime,
680 + key,
681 + );
682 + }
683 + break;
684 }
685 }
686 let info = '';
@@ -742,7 +760,7 @@ export function createFiberFromFragment(
760 }
761
762 export function createFiberFromEventComponent(
745 - eventComponent: ReactEventComponent<any>,
763 + eventComponent: ReactEventComponent<any, any, any>,
764 pendingProps: any,
765 mode: TypeOfMode,
766 expirationTime: ExpirationTime,
@@ -755,6 +773,20 @@ export function createFiberFromEventComponent(
773 return fiber;
774 }
775
776 +export function createFiberFromFundamental(
777 + fundamentalComponent: ReactFundamentalComponent<any, any>,
778 + pendingProps: any,
779 + mode: TypeOfMode,
780 + expirationTime: ExpirationTime,
781 + key: null | string,
782 +): Fiber {
783 + const fiber = createFiber(FundamentalComponent, pendingProps, key, mode);
784 + fiber.elementType = fundamentalComponent;
785 + fiber.type = fundamentalComponent;
786 + fiber.expirationTime = expirationTime;
787 + return fiber;
788 +}
789 +
790 function createFiberFromProfiler(
791 pendingProps: any,
792 mode: TypeOfMode,
packages/react-reconciler/src/ReactFiberBeginWork.js
+34 -1
@@ -42,6 +42,7 @@ import {
42 LazyComponent,
43 IncompleteClassComponent,
44 EventComponent,
45 + FundamentalComponent,
46 } from 'shared/ReactWorkTags';
47 import {
48 NoEffect,
@@ -61,6 +62,7 @@ import {
62 enableSchedulerTracing,
63 enableSuspenseServerRenderer,
64 enableFlareAPI,
65 + enableFundamentalAPI,
66 } from 'shared/ReactFeatureFlags';
67 import invariant from 'shared/invariant';
68 import shallowEqual from 'shared/shallowEqual';
@@ -2479,7 +2481,7 @@ function updateContextConsumer(
2481
2482 function updateEventComponent(current, workInProgress, renderExpirationTime) {
2483 const nextProps = workInProgress.pendingProps;
2482 - let nextChildren = nextProps.children;
2484 + const nextChildren = nextProps.children;
2485
2486 reconcileChildren(
2487 current,
@@ -2491,6 +2493,27 @@ function updateEventComponent(current, workInProgress, renderExpirationTime) {
2493 return workInProgress.child;
2494 }
2495
2496 +function updateFundamentalComponent(
2497 + current,
2498 + workInProgress,
2499 + renderExpirationTime,
2500 +) {
2501 + const fundamentalImpl = workInProgress.type.impl;
2502 + if (fundamentalImpl.reconcileChildren === false) {
2503 + return null;
2504 + }
2505 + const nextProps = workInProgress.pendingProps;
2506 + const nextChildren = nextProps.children;
2507 +
2508 + reconcileChildren(
2509 + current,
2510 + workInProgress,
2511 + nextChildren,
2512 + renderExpirationTime,
2513 + );
2514 + return workInProgress.child;
2515 +}
2516 +
2517 export function markWorkInProgressReceivedUpdate() {
2518 didReceiveUpdate = true;
2519 }
@@ -2980,6 +3003,16 @@ function beginWork(
3003 }
3004 break;
3005 }
3006 + case FundamentalComponent: {
3007 + if (enableFundamentalAPI) {
3008 + return updateFundamentalComponent(
3009 + current,
3010 + workInProgress,
3011 + renderExpirationTime,
3012 + );
3013 + }
3014 + break;
3015 + }
3016 }
3017 invariant(
3018 false,
packages/react-reconciler/src/ReactFiberCommitWork.js
+61 -9
@@ -29,6 +29,7 @@ import {
29 enableProfilerTimer,
30 enableSuspenseServerRenderer,
31 enableFlareAPI,
32 + enableFundamentalAPI,
33 } from 'shared/ReactFeatureFlags';
34 import {
35 FunctionComponent,
@@ -46,6 +47,7 @@ import {
47 SimpleMemoComponent,
48 EventComponent,
49 SuspenseListComponent,
50 + FundamentalComponent,
51 } from 'shared/ReactWorkTags';
52 import {
53 invokeGuardedCallback,
@@ -94,6 +96,8 @@ import {
96 unhideTextInstance,
97 unmountEventComponent,
98 mountEventComponent,
99 + unmountFundamentalComponent,
100 + updateFundamentalComponent,
101 } from './ReactFiberHostConfig';
102 import {
103 captureCommitPhaseError,
@@ -590,6 +594,7 @@ function commitLifeCycles(
594 case SuspenseComponent:
595 case SuspenseListComponent:
596 case IncompleteClassComponent:
597 + case FundamentalComponent:
598 return;
599 case EventComponent: {
600 if (enableFlareAPI) {
@@ -757,6 +762,16 @@ function commitUnmount(current: Fiber): void {
762 unmountEventComponent(eventComponentInstance);
763 current.stateNode = null;
764 }
765 + break;
766 + }
767 + case FundamentalComponent: {
768 + if (enableFundamentalAPI) {
769 + const fundamentalInstance = current.stateNode;
770 + if (fundamentalInstance !== null) {
771 + unmountFundamentalComponent(fundamentalInstance);
772 + current.stateNode = null;
773 + }
774 + }
775 }
776 }
777 }
@@ -838,6 +853,7 @@ function commitContainer(finishedWork: Fiber) {
853 case ClassComponent:
854 case HostComponent:
855 case HostText:
856 + case FundamentalComponent:
857 case EventComponent: {
858 return;
859 }
@@ -942,20 +958,26 @@ function commitPlacement(finishedWork: Fiber): void {
958 // Note: these two variables *must* always be updated together.
959 let parent;
960 let isContainer;
945 -
961 + const parentStateNode = parentFiber.stateNode;
962 switch (parentFiber.tag) {
963 case HostComponent:
948 - parent = parentFiber.stateNode;
964 + parent = parentStateNode;
965 isContainer = false;
966 break;
967 case HostRoot:
952 - parent = parentFiber.stateNode.containerInfo;
968 + parent = parentStateNode.containerInfo;
969 isContainer = true;
970 break;
971 case HostPortal:
956 - parent = parentFiber.stateNode.containerInfo;
972 + parent = parentStateNode.containerInfo;
973 isContainer = true;
974 break;
975 + case FundamentalComponent:
976 + if (enableFundamentalAPI) {
977 + parent = parentStateNode.instance;
978 + isContainer = false;
979 + }
980 + // eslint-disable-next-line-no-fallthrough
981 default:
982 invariant(
983 false,
@@ -975,8 +997,9 @@ function commitPlacement(finishedWork: Fiber): void {
997 // children to find all the terminal nodes.
998 let node: Fiber = finishedWork;
999 while (true) {
978 - if (node.tag === HostComponent || node.tag === HostText) {
979 - const stateNode = node.stateNode;
1000 + const isHost = node.tag === HostComponent || node.tag === HostText;
1001 + if (isHost || node.tag === FundamentalComponent) {
1002 + const stateNode = isHost ? node.stateNode : node.stateNode.instance;
1003 if (before) {
1004 if (isContainer) {
1005 insertInContainerBefore(parent, stateNode, before);
@@ -1035,19 +1058,25 @@ function unmountHostComponents(current): void {
1058 'Expected to find a host parent. This error is likely caused by ' +
1059 'a bug in React. Please file an issue.',
1060 );
1061 + const parentStateNode = parent.stateNode;
1062 switch (parent.tag) {
1063 case HostComponent:
1040 - currentParent = parent.stateNode;
1064 + currentParent = parentStateNode;
1065 currentParentIsContainer = false;
1066 break findParent;
1067 case HostRoot:
1044 - currentParent = parent.stateNode.containerInfo;
1068 + currentParent = parentStateNode.containerInfo;
1069 currentParentIsContainer = true;
1070 break findParent;
1071 case HostPortal:
1048 - currentParent = parent.stateNode.containerInfo;
1072 + currentParent = parentStateNode.containerInfo;
1073 currentParentIsContainer = true;
1074 break findParent;
1075 + case FundamentalComponent:
1076 + if (enableFundamentalAPI) {
1077 + currentParent = parentStateNode.instance;
1078 + currentParentIsContainer = false;
1079 + }
1080 }
1081 parent = parent.return;
1082 }
@@ -1070,6 +1099,22 @@ function unmountHostComponents(current): void {
1099 );
1100 }
1101 // Don't visit children because we already visited them.
1102 + } else if (node.tag === FundamentalComponent) {
1103 + const fundamentalNode = node.stateNode.instance;
1104 + commitNestedUnmounts(node);
1105 + // After all the children have unmounted, it is now safe to remove the
1106 + // node from the tree.
1107 + if (currentParentIsContainer) {
1108 + removeChildFromContainer(
1109 + ((currentParent: any): Container),
1110 + (fundamentalNode: Instance),
1111 + );
1112 + } else {
1113 + removeChild(
1114 + ((currentParent: any): Instance),
1115 + (fundamentalNode: Instance),
1116 + );
1117 + }
1118 } else if (
1119 enableSuspenseServerRenderer &&
1120 node.tag === DehydratedSuspenseComponent
@@ -1243,6 +1288,13 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
1288 case EventComponent: {
1289 return;
1290 }
1291 + case FundamentalComponent: {
1292 + if (enableFundamentalAPI) {
1293 + const fundamentalInstance = finishedWork.stateNode;
1294 + updateFundamentalComponent(fundamentalInstance);
1295 + }
1296 + return;
1297 + }
1298 default: {
1299 invariant(
1300 false,
packages/react-reconciler/src/ReactFiberCompleteWork.js
+82 -1
@@ -17,7 +17,10 @@ import type {
17 Container,
18 ChildSet,
19 } from './ReactFiberHostConfig';
20 -import type {ReactEventComponentInstance} from 'shared/ReactTypes';
20 +import type {
21 + ReactEventComponentInstance,
22 + ReactFundamentalComponentInstance,
23 +} from 'shared/ReactTypes';
24 import type {
25 SuspenseState,
26 SuspenseListRenderState,
@@ -48,6 +51,7 @@ import {
51 LazyComponent,
52 IncompleteClassComponent,
53 EventComponent,
54 + FundamentalComponent,
55 } from 'shared/ReactWorkTags';
56 import {NoMode, BatchedMode} from './ReactTypeOfMode';
57 import {
@@ -75,6 +79,10 @@ import {
79 appendChildToContainerChildSet,
80 finalizeContainerChildren,
81 updateEventComponent,
82 + getFundamentalComponentInstance,
83 + mountFundamentalComponent,
84 + cloneFundamentalInstance,
85 + shouldUpdateFundamentalComponent,
86 } from './ReactFiberHostConfig';
87 import {
88 getRootHostContainer,
@@ -109,6 +117,7 @@ import {
117 enableSchedulerTracing,
118 enableSuspenseServerRenderer,
119 enableFlareAPI,
120 + enableFundamentalAPI,
121 } from 'shared/ReactFeatureFlags';
122 import {
123 markSpawnedWork,
@@ -117,6 +126,7 @@ import {
126 renderHasNotSuspendedYet,
127 } from './ReactFiberWorkLoop';
128 import {createEventComponentInstance} from './ReactFiberEvents';
129 +import {createFundamentalStateInstance} from './ReactFiberFundamental';
130 import {Never} from './ReactFiberExpirationTime';
131 import {resetChildFibers} from './ReactChildFiber';
132
@@ -149,6 +159,8 @@ if (supportsMutation) {
159 while (node !== null) {
160 if (node.tag === HostComponent || node.tag === HostText) {
161 appendInitialChild(parent, node.stateNode);
162 + } else if (node.tag === FundamentalComponent) {
163 + appendInitialChild(parent, node.stateNode.instance);
164 } else if (node.tag === HostPortal) {
165 // If we have a portal child, then we don't want to traverse
166 // down its children. Instead, we'll get insertions from each child in
@@ -258,6 +270,15 @@ if (supportsMutation) {
270 instance = cloneHiddenTextInstance(instance, text, node);
271 }
272 appendInitialChild(parent, instance);
273 + } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {
274 + let instance = node.stateNode.instance;
275 + if (needsVisibilityToggle && isHidden) {
276 + // This child is inside a timed out tree. Hide it.
277 + const props = node.memoizedProps;
278 + const type = node.type;
279 + instance = cloneHiddenInstance(instance, type, props, node);
280 + }
281 + appendInitialChild(parent, instance);
282 } else if (node.tag === HostPortal) {
283 // If we have a portal child, then we don't want to traverse
284 // down its children. Instead, we'll get insertions from each child in
@@ -343,6 +364,15 @@ if (supportsMutation) {
364 instance = cloneHiddenTextInstance(instance, text, node);
365 }
366 appendChildToContainerChildSet(containerChildSet, instance);
367 + } else if (enableFundamentalAPI && node.tag === FundamentalComponent) {
368 + let instance = node.stateNode.instance;
369 + if (needsVisibilityToggle && isHidden) {
370 + // This child is inside a timed out tree. Hide it.
371 + const props = node.memoizedProps;
372 + const type = node.type;
373 + instance = cloneHiddenInstance(instance, type, props, node);
374 + }
375 + appendChildToContainerChildSet(containerChildSet, instance);
376 } else if (node.tag === HostPortal) {
377 // If we have a portal child, then we don't want to traverse
378 // down its children. Instead, we'll get insertions from each child in
@@ -1139,6 +1169,57 @@ function completeWork(
1169 }
1170 break;
1171 }
1172 + case FundamentalComponent: {
1173 + if (enableFundamentalAPI) {
1174 + const fundamentalImpl = workInProgress.type.impl;
1175 + let fundamentalInstance: ReactFundamentalComponentInstance<
1176 + any,
1177 + any,
1178 + > | null =
1179 + workInProgress.stateNode;
1180 +
1181 + if (fundamentalInstance === null) {
1182 + const getInitialState = fundamentalImpl.getInitialState;
1183 + let fundamentalState;
1184 + if (getInitialState !== undefined) {
1185 + fundamentalState = getInitialState(newProps);
1186 + }
1187 + fundamentalInstance = workInProgress.stateNode = createFundamentalStateInstance(
1188 + workInProgress,
1189 + newProps,
1190 + fundamentalImpl,
1191 + fundamentalState || {},
1192 + );
1193 + const instance = ((getFundamentalComponentInstance(
1194 + fundamentalInstance,
1195 + ): any): Instance);
1196 + fundamentalInstance.instance = instance;
1197 + if (fundamentalImpl.reconcileChildren === false) {
1198 + return null;
1199 + }
1200 + appendAllChildren(instance, workInProgress, false, false);
1201 + mountFundamentalComponent(fundamentalInstance);
1202 + } else {
1203 + // We fire update in commit phase
1204 + const prevProps = fundamentalInstance.props;
1205 + fundamentalInstance.prevProps = prevProps;
1206 + fundamentalInstance.props = newProps;
1207 + fundamentalInstance.currentFiber = workInProgress;
1208 + if (supportsPersistence) {
1209 + const instance = cloneFundamentalInstance(fundamentalInstance);
1210 + fundamentalInstance.instance = instance;
1211 + appendAllChildren(instance, workInProgress, false, false);
1212 + }
1213 + const shouldUpdate = shouldUpdateFundamentalComponent(
1214 + fundamentalInstance,
1215 + );
1216 + if (shouldUpdate) {
1217 + markUpdate(workInProgress);
1218 + }
1219 + }
1220 + }
1221 + break;
1222 + }
1223 default:
1224 invariant(
1225 false,
packages/react-reconciler/src/ReactFiberFundamental.js new
+30
@@ -0,0 +1,30 @@
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 + ReactFundamentalImpl,
13 + ReactFundamentalComponentInstance,
14 +} from 'shared/ReactTypes';
15 +
16 +export function createFundamentalStateInstance<C, H>(
17 + currentFiber: Fiber,
18 + props: Object,
19 + impl: ReactFundamentalImpl<C, H>,
20 + state: Object,
21 +): ReactFundamentalComponentInstance<C, H> {
22 + return {
23 + currentFiber,
24 + impl,
25 + instance: null,
26 + prevProps: null,
27 + props,
28 + state,
29 + };
30 +}
packages/react-reconciler/src/ReactFiberReconciler.js
+4
@@ -16,6 +16,7 @@ import type {
16 Container,
17 PublicInstance,
18 } from './ReactFiberHostConfig';
19 +import {FundamentalComponent} from 'shared/ReactWorkTags';
20 import type {ReactNodeList} from 'shared/ReactTypes';
21 import type {ExpirationTime} from './ReactFiberExpirationTime';
22 import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
@@ -374,6 +375,9 @@ export function findHostInstanceWithNoPortals(
375 if (hostFiber === null) {
376 return null;
377 }
378 + if (hostFiber.tag === FundamentalComponent) {
379 + return hostFiber.stateNode.instance;
380 + }
381 return hostFiber.stateNode;
382 }
383
packages/react-reconciler/src/ReactFiberTreeReflection.js
+6 -1
@@ -21,6 +21,7 @@ import {
21 HostRoot,
22 HostPortal,
23 HostText,
24 + FundamentalComponent,
25 } from 'shared/ReactWorkTags';
26 import {NoEffect, Placement} from 'shared/ReactSideEffectTags';
27
@@ -277,7 +278,11 @@ export function findCurrentHostFiberWithNoPortals(parent: Fiber): Fiber | null {
278 // Next we'll drill down this component to find the first HostComponent/Text.
279 let node: Fiber = currentParent;
280 while (true) {
280 - if (node.tag === HostComponent || node.tag === HostText) {
281 + if (
282 + node.tag === HostComponent ||
283 + node.tag === HostText ||
284 + node.tag === FundamentalComponent
285 + ) {
286 return node;
287 } else if (node.child && node.tag !== HostPortal) {
288 node.child.return = node;
packages/react-reconciler/src/__tests__/ReactFiberFundamental-test.internal.js new
+225
@@ -0,0 +1,225 @@
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 ReactNoop;
14 +let Scheduler;
15 +let ReactFeatureFlags;
16 +let ReactTestRenderer;
17 +let ReactDOM;
18 +let ReactDOMServer;
19 +
20 +function createReactFundamentalComponent(fundamentalImpl) {
21 + return React.unstable_createFundamental(fundamentalImpl);
22 +}
23 +
24 +function init() {
25 + jest.resetModules();
26 + ReactFeatureFlags = require('shared/ReactFeatureFlags');
27 + ReactFeatureFlags.enableFundamentalAPI = true;
28 + React = require('react');
29 + Scheduler = require('scheduler');
30 +}
31 +
32 +function initNoopRenderer() {
33 + init();
34 + ReactNoop = require('react-noop-renderer');
35 +}
36 +
37 +function initTestRenderer() {
38 + init();
39 + ReactTestRenderer = require('react-test-renderer');
40 +}
41 +
42 +function initReactDOM() {
43 + init();
44 + ReactDOM = require('react-dom');
45 +}
46 +
47 +function initReactDOMServer() {
48 + init();
49 + ReactDOMServer = require('react-dom/server');
50 +}
51 +
52 +describe('ReactFiberFundamental', () => {
53 + describe('NoopRenderer', () => {
54 + beforeEach(() => {
55 + initNoopRenderer();
56 + });
57 +
58 + it('should render a simple fundamental component with a single child', () => {
59 + const FundamentalComponent = createReactFundamentalComponent({
60 + reconcileChildren: true,
61 + getInstance(context, props, state) {
62 + const instance = {
63 + children: [],
64 + text: null,
65 + type: 'test',
66 + };
67 + return instance;
68 + },
69 + });
70 +
71 + const Test = ({children}) => (
72 + <FundamentalComponent>{children}</FundamentalComponent>
73 + );
74 +
75 + ReactNoop.render(<Test>Hello world</Test>);
76 + expect(Scheduler).toFlushWithoutYielding();
77 + expect(ReactNoop).toMatchRenderedOutput(<test>Hello world</test>);
78 + ReactNoop.render(<Test>Hello world again</Test>);
79 + expect(Scheduler).toFlushWithoutYielding();
80 + expect(ReactNoop).toMatchRenderedOutput(<test>Hello world again</test>);
81 + ReactNoop.render(null);
82 + expect(Scheduler).toFlushWithoutYielding();
83 + expect(ReactNoop).toMatchRenderedOutput(null);
84 + });
85 + });
86 +
87 + describe('NoopTestRenderer', () => {
88 + beforeEach(() => {
89 + initTestRenderer();
90 + });
91 +
92 + it('should render a simple fundamental component with a single child', () => {
93 + const FundamentalComponent = createReactFundamentalComponent({
94 + reconcileChildren: true,
95 + getInstance(context, props, state) {
96 + const instance = {
97 + children: [],
98 + props,
99 + type: 'test',
100 + tag: 'INSTANCE',
101 + };
102 + return instance;
103 + },
104 + });
105 +
106 + const Test = ({children}) => (
107 + <FundamentalComponent>{children}</FundamentalComponent>
108 + );
109 +
110 + const root = ReactTestRenderer.create(null);
111 + root.update(<Test>Hello world</Test>);
112 + expect(Scheduler).toFlushWithoutYielding();
113 + expect(root).toMatchRenderedOutput(<test>Hello world</test>);
114 + root.update(<Test>Hello world again</Test>);
115 + expect(Scheduler).toFlushWithoutYielding();
116 + expect(root).toMatchRenderedOutput(<test>Hello world again</test>);
117 + root.update(null);
118 + expect(Scheduler).toFlushWithoutYielding();
119 + expect(root).toMatchRenderedOutput(null);
120 + });
121 + });
122 +
123 + describe('ReactDOM', () => {
124 + beforeEach(() => {
125 + initReactDOM();
126 + });
127 +
128 + it('should render a simple fundamental component with a single child', () => {
129 + const FundamentalComponent = createReactFundamentalComponent({
130 + reconcileChildren: true,
131 + getInstance(context, props, state) {
132 + const instance = document.createElement('div');
133 + return instance;
134 + },
135 + });
136 +
137 + const Test = ({children}) => (
138 + <FundamentalComponent>{children}</FundamentalComponent>
139 + );
140 +
141 + const container = document.createElement('div');
142 + ReactDOM.render(<Test>Hello world</Test>, container);
143 + expect(Scheduler).toFlushWithoutYielding();
144 + expect(container.innerHTML).toBe('<div>Hello world</div>');
145 + ReactDOM.render(<Test>Hello world again</Test>, container);
146 + expect(Scheduler).toFlushWithoutYielding();
147 + expect(container.innerHTML).toBe('<div>Hello world again</div>');
148 + ReactDOM.render(null, container);
149 + expect(Scheduler).toFlushWithoutYielding();
150 + expect(container.innerHTML).toBe('');
151 + });
152 +
153 + it('should render a simple fundamental component without reconcileChildren', () => {
154 + const FundamentalComponent = createReactFundamentalComponent({
155 + reconcileChildren: false,
156 + getInstance(context, props, state) {
157 + const instance = document.createElement('div');
158 + instance.textContent = 'Hello world';
159 + return instance;
160 + },
161 + });
162 +
163 + const Test = () => <FundamentalComponent />;
164 +
165 + const container = document.createElement('div');
166 + ReactDOM.render(<Test />, container);
167 + expect(Scheduler).toFlushWithoutYielding();
168 + expect(container.innerHTML).toBe('<div>Hello world</div>');
169 + // Children should be ignored
170 + ReactDOM.render(<Test>Hello world again</Test>, container);
171 + expect(Scheduler).toFlushWithoutYielding();
172 + expect(container.innerHTML).toBe('<div>Hello world</div>');
173 + ReactDOM.render(null, container);
174 + expect(Scheduler).toFlushWithoutYielding();
175 + expect(container.innerHTML).toBe('');
176 + });
177 + });
178 +
179 + describe('ReactDOMServer', () => {
180 + beforeEach(() => {
181 + initReactDOMServer();
182 + });
183 +
184 + it('should render a simple fundamental component with a single child', () => {
185 + const getInstance = jest.fn();
186 + const FundamentalComponent = createReactFundamentalComponent({
187 + reconcileChildren: true,
188 + getInstance,
189 + getServerSideString(context, props) {
190 + return `<div>`;
191 + },
192 + getServerSideStringClose(context, props) {
193 + return `</div>`;
194 + },
195 + });
196 +
197 + const Test = ({children}) => (
198 + <FundamentalComponent>{children}</FundamentalComponent>
199 + );
200 +
201 + expect(getInstance).not.toBeCalled();
202 + let output = ReactDOMServer.renderToString(<Test>Hello world</Test>);
203 + expect(output).toBe('<div>Hello world</div>');
204 + output = ReactDOMServer.renderToString(<Test>Hello world again</Test>);
205 + expect(output).toBe('<div>Hello world again</div>');
206 + });
207 +
208 + it('should render a simple fundamental component without reconcileChildren', () => {
209 + const FundamentalComponent = createReactFundamentalComponent({
210 + reconcileChildren: false,
211 + getServerSideString(context, props) {
212 + return `<div>Hello world</div>`;
213 + },
214 + });
215 +
216 + const Test = () => <FundamentalComponent />;
217 +
218 + let output = ReactDOMServer.renderToString(<Test />);
219 + expect(output).toBe('<div>Hello world</div>');
220 + // Children should be ignored
221 + output = ReactDOMServer.renderToString(<Test>Hello world again</Test>);
222 + expect(output).toBe('<div>Hello world</div>');
223 + });
224 + });
225 +});
packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
+11 -1
@@ -65,7 +65,12 @@ export const supportsPersistence = $$$hostConfig.supportsPersistence;
65 export const supportsHydration = $$$hostConfig.supportsHydration;
66 export const mountEventComponent = $$$hostConfig.mountEventComponent;
67 export const updateEventComponent = $$$hostConfig.updateEventComponent;
68 -export const handleEventTarget = $$$hostConfig.handleEventTarget;
68 +export const getFundamentalComponentInstance =
69 + $$$hostConfig.getFundamentalComponentInstance;
70 +export const mountFundamentalComponent =
71 + $$$hostConfig.mountFundamentalComponent;
72 +export const shouldUpdateFundamentalComponent =
73 + $$$hostConfig.shouldUpdateFundamentalComponent;
74
75 // -------------------
76 // Mutation
@@ -87,6 +92,10 @@ export const unhideInstance = $$$hostConfig.unhideInstance;
92 export const unhideTextInstance = $$$hostConfig.unhideTextInstance;
93 export const unmountEventComponent = $$$hostConfig.unmountEventComponent;
94 export const commitEventTarget = $$$hostConfig.commitEventTarget;
95 +export const updateFundamentalComponent =
96 + $$$hostConfig.updateFundamentalComponent;
97 +export const unmountFundamentalComponent =
98 + $$$hostConfig.unmountFundamentalComponent;
99
100 // -------------------
101 // Persistence
@@ -101,6 +110,7 @@ export const finalizeContainerChildren =
110 export const replaceContainerChildren = $$$hostConfig.replaceContainerChildren;
111 export const cloneHiddenInstance = $$$hostConfig.cloneHiddenInstance;
112 export const cloneHiddenTextInstance = $$$hostConfig.cloneHiddenTextInstance;
113 +export const cloneFundamentalInstance = $$$hostConfig.cloneInstance;
114
115 // -------------------
116 // Hydration
packages/react-test-renderer/src/ReactTestHostConfig.js
+50 -1
@@ -9,7 +9,10 @@
9
10 import warning from 'shared/warning';
11
12 -import type {ReactEventComponentInstance} from 'shared/ReactTypes';
12 +import type {
13 + ReactEventComponentInstance,
14 + ReactFundamentalComponentInstance,
15 +} from 'shared/ReactTypes';
16
17 import {enableFlareAPI} from 'shared/ReactFeatureFlags';
18
@@ -302,3 +305,49 @@ export function unmountEventComponent(
305 ): void {
306 // noop
307 }
308 +
309 +export function getFundamentalComponentInstance(fundamentalInstance): Instance {
310 + const {impl, props, state} = fundamentalInstance;
311 + return impl.getInstance(null, props, state);
312 +}
313 +
314 +export function mountFundamentalComponent(
315 + fundamentalInstance: ReactFundamentalComponentInstance<any, any>,
316 +): void {
317 + const {impl, instance, props, state} = fundamentalInstance;
318 + const onMount = impl.onMount;
319 + if (onMount !== undefined) {
320 + onMount(null, instance, props, state);
321 + }
322 +}
323 +
324 +export function shouldUpdateFundamentalComponent(
325 + fundamentalInstance: ReactFundamentalComponentInstance<any, any>,
326 +): boolean {
327 + const {impl, prevProps, props, state} = fundamentalInstance;
328 + const shouldUpdate = impl.shouldUpdate;
329 + if (shouldUpdate !== undefined) {
330 + return shouldUpdate(null, prevProps, props, state);
331 + }
332 + return true;
333 +}
334 +
335 +export function updateFundamentalComponent(
336 + fundamentalInstance: ReactFundamentalComponentInstance<any, any>,
337 +): void {
338 + const {impl, instance, prevProps, props, state} = fundamentalInstance;
339 + const onUpdate = impl.onUpdate;
340 + if (onUpdate !== undefined) {
341 + onUpdate(null, instance, prevProps, props, state);
342 + }
343 +}
344 +
345 +export function unmountFundamentalComponent(
346 + fundamentalInstance: ReactFundamentalComponentInstance<any, any>,
347 +): void {
348 + const {impl, instance, props, state} = fundamentalInstance;
349 + const onUnmount = impl.onUnmount;
350 + if (onUnmount !== undefined) {
351 + onUnmount(null, instance, props, state);
352 + }
353 +}
packages/react/src/React.js
+10 -1
@@ -52,7 +52,12 @@ import {
52 } from './ReactElementValidator';
53 import ReactSharedInternals from './ReactSharedInternals';
54 import createEvent from 'shared/createEventComponent';
55 -import {enableJSXTransformAPI, enableFlareAPI} from 'shared/ReactFeatureFlags';
55 +import createFundamental from 'shared/createFundamentalComponent';
56 +import {
57 + enableJSXTransformAPI,
58 + enableFlareAPI,
59 + enableFundamentalAPI,
60 +} from 'shared/ReactFeatureFlags';
61 const React = {
62 Children: {
63 map,
@@ -105,6 +110,10 @@ if (enableFlareAPI) {
110 React.unstable_useEvent = useEvent;
111 }
112
113 +if (enableFundamentalAPI) {
114 + React.unstable_createFundamental = createFundamental;
115 +}
116 +
117 // Note: some APIs are added with feature flags.
118 // Make sure that stable builds for open source
119 // don't modify the React object to avoid deopts.
packages/shared/HostConfigWithNoPersistence.js
+1
@@ -24,6 +24,7 @@ function shim(...args: any) {
24 // Persistence (when unsupported)
25 export const supportsPersistence = false;
26 export const cloneInstance = shim;
27 +export const cloneFundamentalInstance = shim;
28 export const createContainerChildSet = shim;
29 export const appendChildToContainerChildSet = shim;
30 export const finalizeContainerChildren = shim;
packages/shared/ReactDOMTypes.js
+6
@@ -8,6 +8,7 @@
8 */
9
10 import type {
11 + ReactFundamentalComponentInstance,
12 ReactEventResponder,
13 ReactEventComponentInstance,
14 EventPriority,
@@ -44,6 +45,11 @@ export type ReactDOMEventComponentInstance = ReactEventComponentInstance<
45 ReactDOMResponderContext,
46 >;
47
48 +export type ReactDOMFundamentalComponentInstance = ReactFundamentalComponentInstance<
49 + any,
50 + any,
51 +>;
52 +
53 export type ReactDOMResponderContext = {
54 dispatchEvent: (
55 eventObject: Object,
packages/shared/ReactFeatureFlags.js
+3
@@ -62,6 +62,9 @@ export const warnAboutDeprecatedSetNativeProps = false;
62 // Experimental React Flare event system and event components support.
63 export const enableFlareAPI = false;
64
65 +// Experimental Host Component support.
66 +export const enableFundamentalAPI = false;
67 +
68 // New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107
69 export const enableJSXTransformAPI = false;
70
packages/shared/ReactSymbols.js
+3
@@ -54,6 +54,9 @@ export const REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
54 export const REACT_EVENT_COMPONENT_TYPE = hasSymbol
55 ? Symbol.for('react.event_component')
56 : 0xead5;
57 +export const REACT_FUNDAMENTAL_TYPE = hasSymbol
58 + ? Symbol.for('react.fundamental')
59 + : 0xead6;
60
61 const MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
62 const FAUX_ITERATOR_SYMBOL = '@@iterator';
packages/shared/ReactTypes.js
+45
@@ -112,3 +112,48 @@ export opaque type EventPriority = 0 | 1 | 2;
112 export const DiscreteEvent: EventPriority = 0;
113 export const UserBlockingEvent: EventPriority = 1;
114 export const ContinuousEvent: EventPriority = 2;
115 +
116 +export type ReactFundamentalComponentInstance<C, H> = {|
117 + currentFiber: mixed,
118 + instance: mixed,
119 + prevProps: null | Object,
120 + props: Object,
121 + impl: ReactFundamentalImpl<C, H>,
122 + state: Object,
123 +|};
124 +
125 +export type ReactFundamentalImpl<C, H> = {
126 + displayName: string,
127 + reconcileChildren: boolean,
128 + getInitialState?: (props: Object) => Object,
129 + getInstance: (context: C, props: Object, state: Object) => H,
130 + getServerSideString?: (context: C, props: Object) => string,
131 + getServerSideStringClose?: (context: C, props: Object) => string,
132 + onMount: (context: C, instance: mixed, props: Object, state: Object) => void,
133 + shouldUpdate?: (
134 + context: C,
135 + prevProps: null | Object,
136 + nextProps: Object,
137 + state: Object,
138 + ) => boolean,
139 + onUpdate?: (
140 + context: C,
141 + instance: mixed,
142 + prevProps: null | Object,
143 + nextProps: Object,
144 + state: Object,
145 + ) => void,
146 + onUnmount?: (
147 + context: C,
148 + instance: mixed,
149 + props: Object,
150 + state: Object,
151 + ) => void,
152 + onHydrate?: (context: C, props: Object, state: Object) => boolean,
153 + onFocus?: (context: C, props: Object, state: Object) => boolean,
154 +};
155 +
156 +export type ReactFundamentalComponent<C, H> = {|
157 + $$typeof: Symbol | number,
158 + impl: ReactFundamentalImpl<C, H>,
159 +|};
packages/shared/ReactWorkTags.js
+1 -1
@@ -51,5 +51,5 @@ export const LazyComponent = 16;
51 export const IncompleteClassComponent = 17;
52 export const DehydratedSuspenseComponent = 18;
53 export const EventComponent = 19;
54 -export const EventTarget = 20;
54 +export const FundamentalComponent = 20;
55 export const SuspenseListComponent = 21;
packages/shared/createEventComponent.js
+2 -20
@@ -8,25 +8,7 @@
8
9 import type {ReactEventResponder, ReactEventComponent} from 'shared/ReactTypes';
10 import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
11 -
12 -let hasBadMapPolyfill;
13 -
14 -if (__DEV__) {
15 - hasBadMapPolyfill = false;
16 - try {
17 - const frozenObject = Object.freeze({});
18 - const testMap = new Map([[frozenObject, null]]);
19 - const testSet = new Set([frozenObject]);
20 - // This is necessary for Rollup to not consider these unused.
21 - // https://github.com/rollup/rollup/issues/1771
22 - // TODO: we can remove these if Rollup fixes the bug.
23 - testMap.set(0, 0);
24 - testSet.add(0);
25 - } catch (e) {
26 - // TODO: Consider warning about bad polyfills
27 - hasBadMapPolyfill = true;
28 - }
29 -}
11 +import {hasBadMapPolyfill} from './hasBadMapPolyfill';
12
13 export default function createEventComponent<E, C>(
14 responder: ReactEventResponder<E, C>,
@@ -39,7 +21,7 @@ export default function createEventComponent<E, C>(
21 }
22 const eventComponent = {
23 $$typeof: REACT_EVENT_COMPONENT_TYPE,
42 - responder: responder,
24 + responder,
25 };
26 if (__DEV__) {
27 Object.freeze(eventComponent);
packages/shared/createFundamentalComponent.js new
+33
@@ -0,0 +1,33 @@
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 {
10 + ReactFundamentalImpl,
11 + ReactFundamentalComponent,
12 +} from 'shared/ReactTypes';
13 +import {REACT_FUNDAMENTAL_TYPE} from 'shared/ReactSymbols';
14 +import {hasBadMapPolyfill} from './hasBadMapPolyfill';
15 +
16 +export default function createFundamentalComponent<C, H>(
17 + impl: ReactFundamentalImpl<C, H>,
18 +): ReactFundamentalComponent<C, H> {
19 + // We use responder as a Map key later on. When we have a bad
20 + // polyfill, then we can't use it as a key as the polyfill tries
21 + // to add a property to the object.
22 + if (__DEV__ && !hasBadMapPolyfill) {
23 + Object.freeze(impl);
24 + }
25 + const fundamantalComponent = {
26 + $$typeof: REACT_FUNDAMENTAL_TYPE,
27 + impl,
28 + };
29 + if (__DEV__) {
30 + Object.freeze(fundamantalComponent);
31 + }
32 + return fundamantalComponent;
33 +}
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -32,6 +32,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
32 export const warnAboutDeprecatedLifecycles = true;
33 export const warnAboutDeprecatedSetNativeProps = true;
34 export const enableFlareAPI = false;
35 +export const enableFundamentalAPI = false;
36 export const enableJSXTransformAPI = false;
37 export const warnAboutMissingMockScheduler = true;
38 export const revertPassiveEffectsChange = false;
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -27,6 +27,7 @@ export const warnAboutShorthandPropertyCollision = false;
27 export const enableSchedulerDebugging = false;
28 export const warnAboutDeprecatedSetNativeProps = false;
29 export const enableFlareAPI = false;
30 +export const enableFundamentalAPI = false;
31 export const enableJSXTransformAPI = false;
32 export const warnAboutMissingMockScheduler = false;
33 export const revertPassiveEffectsChange = false;
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -27,6 +27,7 @@ export const warnAboutShorthandPropertyCollision = false;
27 export const enableSchedulerDebugging = false;
28 export const warnAboutDeprecatedSetNativeProps = false;
29 export const enableFlareAPI = false;
30 +export const enableFundamentalAPI = false;
31 export const enableJSXTransformAPI = false;
32 export const warnAboutMissingMockScheduler = true;
33 export const revertPassiveEffectsChange = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -27,6 +27,7 @@ export const warnAboutShorthandPropertyCollision = false;
27 export const enableSchedulerDebugging = false;
28 export const warnAboutDeprecatedSetNativeProps = false;
29 export const enableFlareAPI = false;
30 +export const enableFundamentalAPI = false;
31 export const enableJSXTransformAPI = false;
32 export const warnAboutMissingMockScheduler = false;
33 export const revertPassiveEffectsChange = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -28,6 +28,7 @@ export const enableSchedulerDebugging = false;
28 export const warnAboutDeprecatedSetNativeProps = false;
29 export const disableJavaScriptURLs = false;
30 export const enableFlareAPI = true;
31 +export const enableFundamentalAPI = false;
32 export const enableJSXTransformAPI = true;
33 export const warnAboutMissingMockScheduler = true;
34 export const enableUserBlockingEvents = false;
packages/shared/forks/ReactFeatureFlags.www.js
+2
@@ -70,6 +70,8 @@ function updateFlagOutsideOfReactCallStack() {
70
71 export const enableFlareAPI = true;
72
73 +export const enableFundamentalAPI = false;
74 +
75 export const enableJSXTransformAPI = true;
76
77 export const warnAboutMissingMockScheduler = true;
packages/shared/hasBadMapPolyfill.js new
+26
@@ -0,0 +1,26 @@
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 +export let hasBadMapPolyfill;
10 +
11 +if (__DEV__) {
12 + hasBadMapPolyfill = false;
13 + try {
14 + const frozenObject = Object.freeze({});
15 + const testMap = new Map([[frozenObject, null]]);
16 + const testSet = new Set([frozenObject]);
17 + // This is necessary for Rollup to not consider these unused.
18 + // https://github.com/rollup/rollup/issues/1771
19 + // TODO: we can remove these if Rollup fixes the bug.
20 + testMap.set(0, 0);
21 + testSet.add(0);
22 + } catch (e) {
23 + // TODO: Consider warning about bad polyfills
24 + hasBadMapPolyfill = true;
25 + }
26 +}
packages/shared/isValidElementType.js
+3 -1
@@ -20,6 +20,7 @@ import {
20 REACT_MEMO_TYPE,
21 REACT_LAZY_TYPE,
22 REACT_EVENT_COMPONENT_TYPE,
23 + REACT_FUNDAMENTAL_TYPE,
24 } from 'shared/ReactSymbols';
25
26 export default function isValidElementType(type: mixed) {
@@ -40,6 +41,7 @@ export default function isValidElementType(type: mixed) {
41 type.$$typeof === REACT_PROVIDER_TYPE ||
42 type.$$typeof === REACT_CONTEXT_TYPE ||
43 type.$$typeof === REACT_FORWARD_REF_TYPE ||
43 - type.$$typeof === REACT_EVENT_COMPONENT_TYPE))
44 + type.$$typeof === REACT_EVENT_COMPONENT_TYPE ||
45 + type.$$typeof === REACT_FUNDAMENTAL_TYPE))
46 );
47 }
scripts/error-codes/codes.json
+2 -1
@@ -334,5 +334,6 @@
334 "333": "This should have a parent host component initialized. This error is likely caused by a bug in React. Please file an issue.",
335 "334": "accumulate(...): Accumulated items must not be null or undefined.",
336 "335": "ReactDOMServer does not yet support the event API.",
337 - "336": "The \"%s\" event responder cannot be used via the \"useEvent\" hook."
337 + "336": "The \"%s\" event responder cannot be used via the \"useEvent\" hook.",
338 + "337": "ReactDOMServer does not yet support the fundamental API."
339 }