main
js 179 lines 5.77 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and 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 {ReactNodeList} from 'shared/ReactTypes';
11
12 import {disableLegacyMode} from 'shared/ReactFeatureFlags';
13 import {isValidContainer} from 'react-dom-bindings/src/client/ReactDOMContainer';
14 import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle';
15 import {runWithPriority} from 'react-dom-bindings/src/client/ReactDOMUpdatePriority';
16 import {flushSync as flushSyncIsomorphic} from '../shared/ReactDOMFlushSync';
17
18 import {
19 flushSyncFromReconciler as flushSyncWithoutWarningIfAlreadyRendering,
20 isAlreadyRendering,
21 injectIntoDevTools,
22 findHostInstance,
23 } from 'react-reconciler/src/ReactFiberReconciler';
24 import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
25 import {canUseDOM} from 'shared/ExecutionEnvironment';
26 import ReactVersion from 'shared/ReactVersion';
27
28 import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
29 ensureCorrectIsomorphicReactVersion();
30
31 import {browser} from '../shared/ReactDOMBrowser';
32 import {
33 getInstanceFromNode,
34 getNodeFromInstance,
35 getFiberCurrentPropsFromNode,
36 } from 'react-dom-bindings/src/client/ReactDOMComponentTree';
37 import {
38 enqueueStateRestore,
39 restoreStateIfNeeded,
40 } from 'react-dom-bindings/src/events/ReactDOMControlledComponent';
41 import Internals from '../ReactDOMSharedInternalsFB';
42
43 export {
44 prefetchDNS,
45 preconnect,
46 preload,
47 preloadModule,
48 preinit,
49 preinitModule,
50 } from '../shared/ReactDOMFloat';
51 export {
52 useFormStatus,
53 useFormState,
54 requestFormReset,
55 } from 'react-dom-bindings/src/shared/ReactDOMFormActions';
56
57 if (__DEV__) {
58 if (
59 typeof Map !== 'function' ||
60 // $FlowFixMe[prop-missing] Flow incorrectly thinks Map has no prototype
61 Map.prototype == null ||
62 typeof Map.prototype.forEach !== 'function' ||
63 typeof Set !== 'function' ||
64 // $FlowFixMe[prop-missing] Flow incorrectly thinks Set has no prototype
65 Set.prototype == null ||
66 typeof Set.prototype.clear !== 'function' ||
67 typeof Set.prototype.forEach !== 'function'
68 ) {
69 console.error(
70 'React depends on Map and Set built-in types. Make sure that you load a ' +
71 'polyfill in older browsers. https://react.dev/link/react-polyfills',
72 );
73 }
74 }
75
76 function createPortal(
77 children: ReactNodeList,
78 container: Element | DocumentFragment,
79 key: ?string = null,
80 ): React$Portal {
81 if (!isValidContainer(container)) {
82 throw new Error('Target container is not a DOM element.');
83 }
84
85 // TODO: pass ReactDOM portal implementation as third argument
86 // $FlowFixMe[incompatible-type] The Flow type is opaque but there's no way to actually create it.
87 return createPortalImpl(children, container, null, key);
88 }
89
90 // Overload the definition to the two valid signatures.
91 // Warning, this opts-out of checking the function body.
92 declare function flushSyncFromReconciler<R>(fn: () => R): R;
93 declare function flushSyncFromReconciler(): void;
94 function flushSyncFromReconciler<R>(fn: (() => R) | void): R | void {
95 if (__DEV__) {
96 if (isAlreadyRendering()) {
97 console.error(
98 'flushSync was called from inside a lifecycle method. React cannot ' +
99 'flush when React is already rendering. Consider moving this call to ' +
100 'a scheduler task or micro task.',
101 );
102 }
103 }
104 // $FlowFixMe[incompatible-type]
105 return flushSyncWithoutWarningIfAlreadyRendering(fn);
106 }
107
108 const flushSync: typeof flushSyncIsomorphic = disableLegacyMode
109 ? flushSyncIsomorphic
110 : flushSyncFromReconciler;
111
112 function findDOMNode(
113 componentOrElement: component(...props: any),
114 ): null | Element | Text {
115 return findHostInstance(componentOrElement);
116 }
117
118 // Expose findDOMNode on internals
119 Internals.findDOMNode = findDOMNode;
120
121 function unstable_batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
122 // batchedUpdates was a legacy mode feature that is a no-op outside of
123 // legacy mode. In 19, we made it an actual no-op, but we're keeping it
124 // for now since there may be libraries that still include it.
125 return fn(a);
126 }
127
128 export {
129 browser,
130 createPortal,
131 unstable_batchedUpdates,
132 flushSync,
133 ReactVersion as version,
134 // enableCreateEventHandleAPI
135 createEventHandle as unstable_createEventHandle,
136 // TODO: Remove this once callers migrate to alternatives.
137 // This should only be used by React internals.
138 runWithPriority as unstable_runWithPriority,
139 };
140
141 // Keep in sync with ReactTestUtils.js.
142 // This is an array for better minification.
143 Internals.Events /* Events */ = [
144 getInstanceFromNode,
145 getNodeFromInstance,
146 getFiberCurrentPropsFromNode,
147 enqueueStateRestore,
148 restoreStateIfNeeded,
149 unstable_batchedUpdates,
150 ];
151
152 const foundDevTools = injectIntoDevTools();
153
154 if (__DEV__) {
155 if (!foundDevTools && canUseDOM && window.top === window.self) {
156 // If we're in Chrome or Firefox, provide a download link if not installed.
157 if (
158 (navigator.userAgent.indexOf('Chrome') > -1 &&
159 navigator.userAgent.indexOf('Edge') === -1) ||
160 navigator.userAgent.indexOf('Firefox') > -1
161 ) {
162 const protocol = window.location.protocol;
163 // Don't warn in exotic cases like chrome-extension://.
164 if (/^(https?|file):$/.test(protocol)) {
165 // eslint-disable-next-line react-internal/no-production-logging
166 console.info(
167 '%cDownload the React DevTools ' +
168 'for a better development experience: ' +
169 'https://react.dev/link/react-devtools' +
170 (protocol === 'file:'
171 ? '\nYou might need to use a local HTTP server (instead of file://): ' +
172 'https://react.dev/link/react-devtools-faq'
173 : ''),
174 'font-weight:bold',
175 );
176 }
177 }
178 }
179 }