8
*/
9
10
import type {Container} from './ReactDOMHostConfig';
11
-import type {RootTag} from 'react-reconciler/src/ReactRootTags';
11
import type {MutableSource, ReactNodeList} from 'shared/ReactTypes';
12
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
13
18
...
19
};
20
22
-export type RootOptions = {
21
+export type CreateRootOptions = {
22
+ // TODO: Remove these options.
23
hydrate?: boolean,
24
hydrationOptions?: {
25
onHydrated?: (suspenseNode: Comment) => void,
27
mutableSources?: Array<MutableSource<any>>,
28
...
29
},
30
+ // END OF TODO
31
+ unstable_strictMode?: boolean,
32
+ unstable_concurrentUpdatesByDefault?: boolean,
33
+ ...
34
+};
35
+
36
+export type HydrateRootOptions = {
37
+ // Hydration options
38
+ hydratedSources?: Array<MutableSource<any>>,
39
+ onHydrated?: (suspenseNode: Comment) => void,
40
+ onDeleted?: (suspenseNode: Comment) => void,
41
+ // Options for all roots
42
unstable_strictMode?: boolean,
43
unstable_concurrentUpdatesByDefault?: boolean,
44
...
64
registerMutableSourceForHydration,
65
} from 'react-reconciler/src/ReactFiberReconciler';
66
import invariant from 'shared/invariant';
55
-import {ConcurrentRoot, LegacyRoot} from 'react-reconciler/src/ReactRootTags';
67
+import {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';
68
import {allowConcurrentByDefault} from 'shared/ReactFeatureFlags';
69
58
-function ReactDOMRoot(container: Container, options: void | RootOptions) {
59
- this._internalRoot = createRootImpl(container, ConcurrentRoot, options);
60
-}
61
-
62
-function ReactDOMLegacyRoot(container: Container, options: void | RootOptions) {
63
- this._internalRoot = createRootImpl(container, LegacyRoot, options);
70
+function ReactDOMRoot(internalRoot) {
71
+ this._internalRoot = internalRoot;
72
}
73
66
-ReactDOMRoot.prototype.render = ReactDOMLegacyRoot.prototype.render = function(
67
- children: ReactNodeList,
68
-): void {
74
+ReactDOMRoot.prototype.render = function(children: ReactNodeList): void {
75
const root = this._internalRoot;
76
if (__DEV__) {
77
if (typeof arguments[1] === 'function') {
99
updateContainer(children, root, null, null);
100
};
101
96
-ReactDOMRoot.prototype.unmount = ReactDOMLegacyRoot.prototype.unmount = function(): void {
102
+ReactDOMRoot.prototype.unmount = function(): void {
103
if (__DEV__) {
104
if (typeof arguments[0] === 'function') {
105
console.error(
115
});
116
};
117
112
-function createRootImpl(
118
+export function createRoot(
119
container: Container,
114
- tag: RootTag,
115
- options: void | RootOptions,
116
-) {
117
- // Tag is either LegacyRoot or Concurrent Root
120
+ options?: CreateRootOptions,
121
+): RootType {
122
+ invariant(
123
+ isValidContainerLegacy(container),
124
+ 'createRoot(...): Target container is not a DOM element.',
125
+ );
126
+ warnIfReactDOMContainerInDEV(container);
127
+
128
+ // TODO: Delete these options
129
const hydrate = options != null && options.hydrate === true;
130
const hydrationCallbacks =
131
(options != null && options.hydrationOptions) || null;
134
options.hydrationOptions != null &&
135
options.hydrationOptions.mutableSources) ||
136
null;
126
- const isStrictMode = options != null && options.unstable_strictMode === true;
137
+ // END TODO
138
139
+ const isStrictMode = options != null && options.unstable_strictMode === true;
140
let concurrentUpdatesByDefaultOverride = null;
141
if (allowConcurrentByDefault) {
142
concurrentUpdatesByDefaultOverride =
147
148
const root = createContainer(
149
container,
138
- tag,
150
+ ConcurrentRoot,
151
hydrate,
152
hydrationCallbacks,
153
isStrictMode,
159
container.nodeType === COMMENT_NODE ? container.parentNode : container;
160
listenToAllSupportedEvents(rootContainerElement);
161
162
+ // TODO: Delete this path
163
if (mutableSources) {
164
for (let i = 0; i < mutableSources.length; i++) {
165
const mutableSource = mutableSources[i];
166
registerMutableSourceForHydration(root, mutableSource);
167
}
168
}
169
+ // END TODO
170
157
- return root;
171
+ return new ReactDOMRoot(root);
172
}
173
160
-export function createRoot(
174
+export function hydrateRoot(
175
container: Container,
162
- options?: RootOptions,
176
+ initialChildren: ReactNodeList,
177
+ options?: HydrateRootOptions,
178
): RootType {
179
invariant(
180
isValidContainer(container),
166
- 'createRoot(...): Target container is not a DOM element.',
181
+ 'hydrateRoot(...): Target container is not a DOM element.',
182
);
183
warnIfReactDOMContainerInDEV(container);
169
- return new ReactDOMRoot(container, options);
184
+
185
+ // For now we reuse the whole bag of options since they contain
186
+ // the hydration callbacks.
187
+ const hydrationCallbacks = options != null ? options : null;
188
+ const mutableSources = (options != null && options.hydratedSources) || null;
189
+ const isStrictMode = options != null && options.unstable_strictMode === true;
190
+
191
+ let concurrentUpdatesByDefaultOverride = null;
192
+ if (allowConcurrentByDefault) {
193
+ concurrentUpdatesByDefaultOverride =
194
+ options != null && options.unstable_concurrentUpdatesByDefault != null
195
+ ? options.unstable_concurrentUpdatesByDefault
196
+ : null;
197
+ }
198
+
199
+ const root = createContainer(
200
+ container,
201
+ ConcurrentRoot,
202
+ true, // hydrate
203
+ hydrationCallbacks,
204
+ isStrictMode,
205
+ concurrentUpdatesByDefaultOverride,
206
+ );
207
+ markContainerAsRoot(root.current, container);
208
+ // This can't be a comment node since hydration doesn't work on comment nodes anyway.
209
+ listenToAllSupportedEvents(container);
210
+
211
+ if (mutableSources) {
212
+ for (let i = 0; i < mutableSources.length; i++) {
213
+ const mutableSource = mutableSources[i];
214
+ registerMutableSourceForHydration(root, mutableSource);
215
+ }
216
+ }
217
+
218
+ // Render the initial children
219
+ updateContainer(initialChildren, root, null, null);
220
+
221
+ return new ReactDOMRoot(root);
222
}
223
172
-export function createLegacyRoot(
173
- container: Container,
174
- options?: RootOptions,
175
-): RootType {
176
- return new ReactDOMLegacyRoot(container, options);
224
+export function isValidContainer(node: any): boolean {
225
+ return !!(
226
+ node &&
227
+ (node.nodeType === ELEMENT_NODE ||
228
+ node.nodeType === DOCUMENT_NODE ||
229
+ node.nodeType === DOCUMENT_FRAGMENT_NODE)
230
+ );
231
}
232
179
-export function isValidContainer(node: mixed): boolean {
233
+// TODO: Remove this function which also includes comment nodes.
234
+// We only use it in places that are currently more relaxed.
235
+export function isValidContainerLegacy(node: any): boolean {
236
return !!(
237
node &&
238
(node.nodeType === ELEMENT_NODE ||