Delete Partial Renderer SSR implementation (#24868)
This removes the old server rendering implementation (the "Partial Renderer"). It was replaced in React 18 with a new streaming implementation (Fizz). We hadn't removed it from the codebase yet because Facebook hadn't finished rolling out Fizz in production; it's been behind a feature flag while we run performance tests and migrate our internal infrastructure. The diff to land Fizz will land imminently, and once it does, we can merge this commit.
Andrew Clark committed
Jul 7, 2022 at 16:57 UTC
95e22ff528c875b090de51a0fb0d83181c4d1111
11 files changed
+11
-2518
packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js
+5
-28
@@ -11,7 +11,6 @@ let React;
11
let ReactDOMClient;
12
let ReactDOMServer;
13
let act;
14
-let usingPartialRenderer;
14
15
const util = require('util');
16
const realConsoleError = console.error;
@@ -26,8 +25,6 @@ describe('ReactDOMServerHydration', () => {
25
ReactDOMServer = require('react-dom/server');
26
act = require('react-dom/test-utils').act;
27
29
- usingPartialRenderer = global.__WWW__ && !__EXPERIMENTAL__;
30
-
28
console.error = jest.fn();
29
container = document.createElement('div');
30
document.body.appendChild(container);
@@ -731,15 +728,13 @@ describe('ReactDOMServerHydration', () => {
728
);
729
}
730
734
- // @TODO FB bundles use a different renderer that does not serialize errors to the client
735
- const mismatchEl = usingPartialRenderer ? '<p>' : '<template>';
731
// @TODO changes made to sending Fizz errors to client led to the insertion of templates in client rendered
732
// suspense boundaries. This leaks in this test becuase the client rendered suspense boundary appears like
733
// unhydrated tail nodes and this template is the first match. When we add special case handling for client
734
// rendered suspense boundaries this test will likely change again
735
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
736
Array [
742
- "Warning: Did not expect server HTML to contain a ${mismatchEl} in <div>.
737
+ "Warning: Did not expect server HTML to contain a <template> in <div>.
738
in div (at **)
739
in Mismatch (at **)",
740
"Warning: An error occurred during hydration. The server HTML was replaced with client content in <div>.",
@@ -819,21 +814,12 @@ describe('ReactDOMServerHydration', () => {
814
</div>
815
);
816
}
822
- // We gate this assertion becuase fb-classic uses PartialRenderer for renderToString and it does not
823
- // serialize server errors and send to client
824
- if (usingPartialRenderer) {
825
- expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
826
- Array [
827
- "Caught [The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.]",
828
- ]
829
- `);
830
- } else {
831
- expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
817
+
818
+ expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
819
Array [
820
"Caught [The server did not finish this Suspense boundary: The server used \\"renderToString\\" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to \\"renderToPipeableStream\\" which supports Suspense on the server]",
821
]
822
`);
836
- }
823
});
824
825
// @gate __DEV__
@@ -854,21 +840,12 @@ describe('ReactDOMServerHydration', () => {
840
</div>
841
);
842
}
857
- // We gate this assertion becuase fb-classic uses PartialRenderer for renderToString and it does not
858
- // serialize server errors and send to client
859
- if (usingPartialRenderer) {
860
- expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
861
- Array [
862
- "Caught [The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.]",
863
- ]
864
- `);
865
- } else {
866
- expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
843
+
844
+ expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
845
Array [
846
"Caught [The server did not finish this Suspense boundary: The server used \\"renderToString\\" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to \\"renderToPipeableStream\\" which supports Suspense on the server]",
847
]
848
`);
871
- }
849
});
850
});
851
packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js
+5
-13
@@ -19,7 +19,6 @@ let Suspense;
19
let SuspenseList;
20
let act;
21
let IdleEventPriority;
22
-let usingPartialRenderer;
22
23
function normalizeCodeLocInfo(strOrErr) {
24
if (strOrErr && strOrErr.replace) {
@@ -111,8 +110,6 @@ describe('ReactDOMServerPartialHydration', () => {
110
SuspenseList = React.SuspenseList;
111
}
112
114
- usingPartialRenderer = global.__WWW__ && !__EXPERIMENTAL__;
115
-
113
IdleEventPriority = require('react-reconciler/constants').IdleEventPriority;
114
});
115
@@ -1671,8 +1668,7 @@ describe('ReactDOMServerPartialHydration', () => {
1668
Scheduler.unstable_yieldValue(error.message);
1669
},
1670
});
1674
- // we exclude fb bundles with partial renderer
1675
- if (__DEV__ && !usingPartialRenderer) {
1671
+ if (__DEV__) {
1672
expect(Scheduler).toFlushAndYield([
1673
'The server did not finish this Suspense boundary: The server used' +
1674
' "renderToString" which does not support Suspense. If you intended' +
@@ -1745,8 +1741,7 @@ describe('ReactDOMServerPartialHydration', () => {
1741
Scheduler.unstable_yieldValue(error.message);
1742
},
1743
});
1748
- // we exclude fb bundles with partial renderer
1749
- if (__DEV__ && !usingPartialRenderer) {
1744
+ if (__DEV__) {
1745
expect(Scheduler).toFlushAndYield([
1746
'The server did not finish this Suspense boundary: The server used' +
1747
' "renderToString" which does not support Suspense. If you intended' +
@@ -1824,8 +1819,7 @@ describe('ReactDOMServerPartialHydration', () => {
1819
Scheduler.unstable_yieldValue(error.message);
1820
},
1821
});
1827
- // we exclude fb bundles with partial renderer
1828
- if (__DEV__ && !usingPartialRenderer) {
1822
+ if (__DEV__) {
1823
expect(Scheduler).toFlushAndYield([
1824
'The server did not finish this Suspense boundary: The server used' +
1825
' "renderToString" which does not support Suspense. If you intended' +
@@ -2154,8 +2148,7 @@ describe('ReactDOMServerPartialHydration', () => {
2148
});
2149
2150
suspend = true;
2157
- // we exclude fb bundles with partial renderer
2158
- if (__DEV__ && !usingPartialRenderer) {
2151
+ if (__DEV__) {
2152
expect(Scheduler).toFlushAndYield([
2153
'The server did not finish this Suspense boundary: The server used' +
2154
' "renderToString" which does not support Suspense. If you intended' +
@@ -2229,8 +2222,7 @@ describe('ReactDOMServerPartialHydration', () => {
2222
Scheduler.unstable_yieldValue(error.message);
2223
},
2224
});
2232
- // we exclude fb bundles with partial renderer
2233
- if (__DEV__ && !usingPartialRenderer) {
2225
+ if (__DEV__) {
2226
expect(Scheduler).toFlushAndYield([
2227
'The server did not finish this Suspense boundary: The server used' +
2228
' "renderToString" which does not support Suspense. If you intended' +
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+1
-8
@@ -14,7 +14,6 @@ let React;
14
let ReactDOMServer;
15
let PropTypes;
16
let ReactCurrentDispatcher;
17
-let useingPartialRenderer;
17
18
describe('ReactDOMServer', () => {
19
beforeEach(() => {
@@ -25,8 +24,6 @@ describe('ReactDOMServer', () => {
24
ReactCurrentDispatcher =
25
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
26
.ReactCurrentDispatcher;
28
-
29
- useingPartialRenderer = global.__WWW__ && !__EXPERIMENTAL__;
27
});
28
29
describe('renderToString', () => {
@@ -576,11 +573,7 @@ describe('ReactDOMServer', () => {
573
<Suspender />
574
</React.Suspense>,
575
);
579
- if (useingPartialRenderer) {
580
- expect(response).toEqual('<!--$!-->fallback<!--/$-->');
581
- } else {
582
- expect(response).toEqual('fallback');
583
- }
576
+ expect(response).toEqual('fallback');
577
});
578
});
579
packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
-2
@@ -603,7 +603,6 @@ describe('ReactDOMServerHydration', () => {
603
expect(customElement.obj).toBe(undefined);
604
});
605
606
- // @gate experimental || !www || !__DEV__
606
it('refers users to apis that support Suspense when something suspends', () => {
607
const theInfinitePromise = new Promise(() => {});
608
function InfiniteSuspend() {
@@ -649,7 +648,6 @@ describe('ReactDOMServerHydration', () => {
648
}
649
});
650
652
- // @gate experimental || !www || !__DEV__
651
it('refers users to apis that support Suspense when something suspends (browser)', () => {
652
const theInfinitePromise = new Promise(() => {});
653
function InfiniteSuspend() {
packages/react-dom/src/server/ReactDOMLegacyServerBrowser.classic.fb.js
deleted
-16
@@ -1,16 +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
- *
7
- * @flow
8
- */
9
-
10
-export {
11
- renderToString,
12
- renderToStaticMarkup,
13
- renderToNodeStream,
14
- renderToStaticNodeStream,
15
- version,
16
-} from './ReactDOMServerLegacyPartialRendererBrowser';
packages/react-dom/src/server/ReactDOMLegacyServerNode.classic.fb.js
deleted
-19
@@ -1,19 +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
- *
7
- * @flow
8
- */
9
-
10
-export {
11
- renderToString,
12
- renderToStaticMarkup,
13
- version,
14
-} from './ReactDOMServerLegacyPartialRendererBrowser';
15
-
16
-export {
17
- renderToNodeStream,
18
- renderToStaticNodeStream,
19
-} from './ReactDOMLegacyServerNodeStream';
packages/react-dom/src/server/ReactDOMServerLegacyPartialRendererBrowser.js
deleted
-32
@@ -1,32 +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
- */
7
-
8
-import ReactVersion from 'shared/ReactVersion';
9
-
10
-import {renderToString, renderToStaticMarkup} from './ReactDOMStringRenderer';
11
-
12
-function renderToNodeStream() {
13
- throw new Error(
14
- 'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
15
- 'in the browser. Use ReactDOMServer.renderToString() instead.',
16
- );
17
-}
18
-
19
-function renderToStaticNodeStream() {
20
- throw new Error(
21
- 'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
22
- 'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
23
- );
24
-}
25
-
26
-export {
27
- renderToString,
28
- renderToStaticMarkup,
29
- renderToNodeStream,
30
- renderToStaticNodeStream,
31
- ReactVersion as version,
32
-};
packages/react-dom/src/server/ReactDOMStringRenderer.js
deleted
-39
@@ -1,39 +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
- */
7
-
8
-import type {ServerOptions} from './ReactPartialRenderer';
9
-import ReactPartialRenderer from './ReactPartialRenderer';
10
-
11
-/**
12
- * Render a ReactElement to its initial HTML. This should only be used on the
13
- * server.
14
- * See https://reactjs.org/docs/react-dom-server.html#rendertostring
15
- */
16
-export function renderToString(element, options?: ServerOptions) {
17
- const renderer = new ReactPartialRenderer(element, false, options);
18
- try {
19
- const markup = renderer.read(Infinity);
20
- return markup;
21
- } finally {
22
- renderer.destroy();
23
- }
24
-}
25
-
26
-/**
27
- * Similar to renderToString, except this doesn't create extra DOM attributes
28
- * such as data-react-id that React uses internally.
29
- * See https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup
30
- */
31
-export function renderToStaticMarkup(element, options?: ServerOptions) {
32
- const renderer = new ReactPartialRenderer(element, true, options);
33
- try {
34
- const markup = renderer.read(Infinity);
35
- return markup;
36
- } finally {
37
- renderer.destroy();
38
- }
39
-}
packages/react-dom/src/server/ReactPartialRenderer.js
deleted
-1646
@@ -1,1646 +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
- *
7
- * @flow
8
- */
9
-
10
-import type {ThreadID} from './ReactThreadIDAllocator';
11
-import type {ReactElement} from 'shared/ReactElementType';
12
-import type {LazyComponent} from 'react/src/ReactLazy';
13
-import type {ReactProvider, ReactContext} from 'shared/ReactTypes';
14
-
15
-import * as React from 'react';
16
-import isArray from 'shared/isArray';
17
-import getComponentNameFromType from 'shared/getComponentNameFromType';
18
-import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
19
-import ReactSharedInternals from 'shared/ReactSharedInternals';
20
-import {
21
- warnAboutDeprecatedLifecycles,
22
- disableLegacyContext,
23
- disableModulePatternComponents,
24
- enableScopeAPI,
25
-} from 'shared/ReactFeatureFlags';
26
-import {
27
- checkPropStringCoercion,
28
- checkFormFieldValueStringCoercion,
29
-} from 'shared/CheckStringCoercion';
30
-
31
-import {
32
- REACT_DEBUG_TRACING_MODE_TYPE,
33
- REACT_FORWARD_REF_TYPE,
34
- REACT_FRAGMENT_TYPE,
35
- REACT_STRICT_MODE_TYPE,
36
- REACT_SUSPENSE_TYPE,
37
- REACT_SUSPENSE_LIST_TYPE,
38
- REACT_PORTAL_TYPE,
39
- REACT_PROFILER_TYPE,
40
- REACT_PROVIDER_TYPE,
41
- REACT_CONTEXT_TYPE,
42
- REACT_LAZY_TYPE,
43
- REACT_MEMO_TYPE,
44
- REACT_SCOPE_TYPE,
45
- REACT_LEGACY_HIDDEN_TYPE,
46
-} from 'shared/ReactSymbols';
47
-
48
-import {
49
- emptyObject,
50
- processContext,
51
- validateContextBounds,
52
-} from './ReactPartialRendererContext';
53
-import {allocThreadID, freeThreadID} from './ReactThreadIDAllocator';
54
-import {
55
- createMarkupForCustomAttribute,
56
- createMarkupForProperty,
57
-} from './DOMMarkupOperations';
58
-import escapeTextForBrowser from './escapeTextForBrowser';
59
-import {
60
- prepareToUseHooks,
61
- finishHooks,
62
- resetHooksState,
63
- Dispatcher,
64
- currentPartialRenderer,
65
- setCurrentPartialRenderer,
66
-} from './ReactPartialRendererHooks';
67
-import {
68
- HTML_NAMESPACE,
69
- getIntrinsicNamespace,
70
- getChildNamespace,
71
-} from '../shared/DOMNamespaces';
72
-import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
73
-import assertValidProps from '../shared/assertValidProps';
74
-import dangerousStyleValue from '../shared/dangerousStyleValue';
75
-import hyphenateStyleName from '../shared/hyphenateStyleName';
76
-import isCustomComponentFn from '../shared/isCustomComponent';
77
-import omittedCloseTags from '../shared/omittedCloseTags';
78
-import warnValidStyle from '../shared/warnValidStyle';
79
-import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
80
-import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
81
-import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';
82
-import assign from 'shared/assign';
83
-import hasOwnProperty from 'shared/hasOwnProperty';
84
-
85
-// Based on reading the React.Children implementation. TODO: type this somewhere?
86
-type ReactNode = string | number | ReactElement;
87
-type FlatReactChildren = Array<null | ReactNode>;
88
-type toArrayType = (children: mixed) => FlatReactChildren;
89
-const toArray = ((React.Children.toArray: any): toArrayType);
90
-
91
-// This is only used in DEV.
92
-// Each entry is `this.stack` from a currently executing renderer instance.
93
-// (There may be more than one because ReactDOMServer is reentrant).
94
-// Each stack is an array of frames which may contain nested stacks of elements.
95
-const currentDebugStacks = [];
96
-
97
-const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
98
-let ReactDebugCurrentFrame;
99
-let prevGetCurrentStackImpl = null;
100
-let getCurrentServerStackImpl = () => '';
101
-let describeStackFrame = element => '';
102
-
103
-let validatePropertiesInDevelopment = (type, props) => {};
104
-let pushCurrentDebugStack = (stack: Array<Frame>) => {};
105
-let pushElementToDebugStack = (element: ReactElement) => {};
106
-let popCurrentDebugStack = () => {};
107
-let hasWarnedAboutUsingContextAsConsumer = false;
108
-
109
-if (__DEV__) {
110
- ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
111
-
112
- validatePropertiesInDevelopment = function(type, props) {
113
- validateARIAProperties(type, props);
114
- validateInputProperties(type, props);
115
- validateUnknownProperties(type, props, null);
116
- };
117
-
118
- describeStackFrame = function(element): string {
119
- return describeUnknownElementTypeFrameInDEV(
120
- element.type,
121
- element._source,
122
- null,
123
- );
124
- };
125
-
126
- pushCurrentDebugStack = function(stack: Array<Frame>) {
127
- currentDebugStacks.push(stack);
128
-
129
- if (currentDebugStacks.length === 1) {
130
- // We are entering a server renderer.
131
- // Remember the previous (e.g. client) global stack implementation.
132
- prevGetCurrentStackImpl = ReactDebugCurrentFrame.getCurrentStack;
133
- ReactDebugCurrentFrame.getCurrentStack = getCurrentServerStackImpl;
134
- }
135
- };
136
-
137
- pushElementToDebugStack = function(element: ReactElement) {
138
- // For the innermost executing ReactDOMServer call,
139
- const stack = currentDebugStacks[currentDebugStacks.length - 1];
140
- // Take the innermost executing frame (e.g. <Foo>),
141
- const frame: Frame = stack[stack.length - 1];
142
- // and record that it has one more element associated with it.
143
- ((frame: any): FrameDev).debugElementStack.push(element);
144
- // We only need this because we tail-optimize single-element
145
- // children and directly handle them in an inner loop instead of
146
- // creating separate frames for them.
147
- };
148
-
149
- popCurrentDebugStack = function() {
150
- currentDebugStacks.pop();
151
-
152
- if (currentDebugStacks.length === 0) {
153
- // We are exiting the server renderer.
154
- // Restore the previous (e.g. client) global stack implementation.
155
- ReactDebugCurrentFrame.getCurrentStack = prevGetCurrentStackImpl;
156
- prevGetCurrentStackImpl = null;
157
- }
158
- };
159
-
160
- getCurrentServerStackImpl = function(): string {
161
- if (currentDebugStacks.length === 0) {
162
- // Nothing is currently rendering.
163
- return '';
164
- }
165
- // ReactDOMServer is reentrant so there may be multiple calls at the same time.
166
- // Take the frames from the innermost call which is the last in the array.
167
- const frames = currentDebugStacks[currentDebugStacks.length - 1];
168
- let stack = '';
169
- // Go through every frame in the stack from the innermost one.
170
- for (let i = frames.length - 1; i >= 0; i--) {
171
- const frame: Frame = frames[i];
172
- // Every frame might have more than one debug element stack entry associated with it.
173
- // This is because single-child nesting doesn't create materialized frames.
174
- // Instead it would push them through `pushElementToDebugStack()`.
175
- const debugElementStack = ((frame: any): FrameDev).debugElementStack;
176
- for (let ii = debugElementStack.length - 1; ii >= 0; ii--) {
177
- stack += describeStackFrame(debugElementStack[ii]);
178
- }
179
- }
180
- return stack;
181
- };
182
-}
183
-
184
-let didWarnDefaultInputValue = false;
185
-let didWarnDefaultChecked = false;
186
-let didWarnDefaultSelectValue = false;
187
-let didWarnDefaultTextareaValue = false;
188
-let didWarnInvalidOptionChildren = false;
189
-let didWarnInvalidOptionInnerHTML = false;
190
-const didWarnAboutNoopUpdateForComponent = {};
191
-const didWarnAboutBadClass = {};
192
-const didWarnAboutModulePatternComponent = {};
193
-const didWarnAboutDeprecatedWillMount = {};
194
-const didWarnAboutUndefinedDerivedState = {};
195
-const didWarnAboutUninitializedState = {};
196
-const didWarnAboutLegacyLifecyclesAndDerivedState = {};
197
-const valuePropNames = ['value', 'defaultValue'];
198
-const newlineEatingTags = {
199
- listing: true,
200
- pre: true,
201
- textarea: true,
202
-};
203
-
204
-// We accept any tag to be rendered but since this gets injected into arbitrary
205
-// HTML, we want to make sure that it's a safe tag.
206
-// http://www.w3.org/TR/REC-xml/#NT-Name
207
-const VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset
208
-const validatedTagCache = {};
209
-function validateDangerousTag(tag) {
210
- if (!validatedTagCache.hasOwnProperty(tag)) {
211
- if (!VALID_TAG_REGEX.test(tag)) {
212
- throw new Error(`Invalid tag: ${tag}`);
213
- }
214
-
215
- validatedTagCache[tag] = true;
216
- }
217
-}
218
-
219
-const styleNameCache = {};
220
-const processStyleName = function(styleName) {
221
- if (styleNameCache.hasOwnProperty(styleName)) {
222
- return styleNameCache[styleName];
223
- }
224
- const result = hyphenateStyleName(styleName);
225
- styleNameCache[styleName] = result;
226
- return result;
227
-};
228
-
229
-function createMarkupForStyles(styles): string | null {
230
- let serialized = '';
231
- let delimiter = '';
232
- for (const styleName in styles) {
233
- if (!styles.hasOwnProperty(styleName)) {
234
- continue;
235
- }
236
- const isCustomProperty = styleName.indexOf('--') === 0;
237
- const styleValue = styles[styleName];
238
- if (__DEV__) {
239
- if (!isCustomProperty) {
240
- warnValidStyle(styleName, styleValue);
241
- }
242
- }
243
- if (styleValue != null) {
244
- serialized +=
245
- delimiter +
246
- (isCustomProperty ? styleName : processStyleName(styleName)) +
247
- ':';
248
- serialized += dangerousStyleValue(
249
- styleName,
250
- styleValue,
251
- isCustomProperty,
252
- );
253
-
254
- delimiter = ';';
255
- }
256
- }
257
- return serialized || null;
258
-}
259
-
260
-function warnNoop(
261
- publicInstance: React$Component<any, any>,
262
- callerName: string,
263
-) {
264
- if (__DEV__) {
265
- const constructor = publicInstance.constructor;
266
- const componentName =
267
- (constructor && getComponentNameFromType(constructor)) || 'ReactClass';
268
- const warningKey = componentName + '.' + callerName;
269
- if (didWarnAboutNoopUpdateForComponent[warningKey]) {
270
- return;
271
- }
272
-
273
- console.error(
274
- '%s(...): Can only update a mounting component. ' +
275
- 'This usually means you called %s() outside componentWillMount() on the server. ' +
276
- 'This is a no-op.\n\nPlease check the code for the %s component.',
277
- callerName,
278
- callerName,
279
- componentName,
280
- );
281
- didWarnAboutNoopUpdateForComponent[warningKey] = true;
282
- }
283
-}
284
-
285
-function shouldConstruct(Component) {
286
- return Component.prototype && Component.prototype.isReactComponent;
287
-}
288
-
289
-function getNonChildrenInnerMarkup(props) {
290
- const innerHTML = props.dangerouslySetInnerHTML;
291
- if (innerHTML != null) {
292
- if (innerHTML.__html != null) {
293
- return innerHTML.__html;
294
- }
295
- } else {
296
- const content = props.children;
297
- if (typeof content === 'string' || typeof content === 'number') {
298
- return escapeTextForBrowser(content);
299
- }
300
- }
301
- return null;
302
-}
303
-
304
-function flattenTopLevelChildren(children: mixed): FlatReactChildren {
305
- if (!React.isValidElement(children)) {
306
- return toArray(children);
307
- }
308
- const element = ((children: any): ReactElement);
309
- if (element.type !== REACT_FRAGMENT_TYPE) {
310
- return [element];
311
- }
312
- const fragmentChildren = element.props.children;
313
- if (!React.isValidElement(fragmentChildren)) {
314
- return toArray(fragmentChildren);
315
- }
316
- const fragmentChildElement = ((fragmentChildren: any): ReactElement);
317
- return [fragmentChildElement];
318
-}
319
-
320
-function flattenOptionChildren(children: mixed): ?string {
321
- if (children === undefined || children === null) {
322
- return children;
323
- }
324
- let content = '';
325
- // Flatten children and warn if they aren't strings or numbers;
326
- // invalid types are ignored.
327
- React.Children.forEach((children: any), function(child) {
328
- if (child == null) {
329
- return;
330
- }
331
- content += (child: any);
332
- if (__DEV__) {
333
- if (
334
- !didWarnInvalidOptionChildren &&
335
- typeof child !== 'string' &&
336
- typeof child !== 'number'
337
- ) {
338
- didWarnInvalidOptionChildren = true;
339
- console.error(
340
- 'Cannot infer the option value of complex children. ' +
341
- 'Pass a `value` prop or use a plain string as children to <option>.',
342
- );
343
- }
344
- }
345
- });
346
- return content;
347
-}
348
-
349
-const STYLE = 'style';
350
-const RESERVED_PROPS = {
351
- children: null,
352
- dangerouslySetInnerHTML: null,
353
- suppressContentEditableWarning: null,
354
- suppressHydrationWarning: null,
355
-};
356
-
357
-function createOpenTagMarkup(
358
- tagVerbatim: string,
359
- tagLowercase: string,
360
- props: Object,
361
- namespace: string,
362
- makeStaticMarkup: boolean,
363
- isRootElement: boolean,
364
-): string {
365
- let ret = '<' + tagVerbatim;
366
-
367
- const isCustomComponent = isCustomComponentFn(tagLowercase, props);
368
-
369
- for (const propKey in props) {
370
- if (!hasOwnProperty.call(props, propKey)) {
371
- continue;
372
- }
373
- let propValue = props[propKey];
374
- if (propValue == null) {
375
- continue;
376
- }
377
- if (propKey === STYLE) {
378
- propValue = createMarkupForStyles(propValue);
379
- }
380
- let markup = null;
381
- if (isCustomComponent) {
382
- if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
383
- markup = createMarkupForCustomAttribute(propKey, propValue);
384
- }
385
- } else {
386
- markup = createMarkupForProperty(propKey, propValue);
387
- }
388
- if (markup) {
389
- ret += ' ' + markup;
390
- }
391
- }
392
-
393
- return ret;
394
-}
395
-
396
-function resolve(
397
- child: mixed,
398
- context: Object,
399
- threadID: ThreadID,
400
-): {|
401
- child: mixed,
402
- context: Object,
403
-|} {
404
- while (React.isValidElement(child)) {
405
- // Safe because we just checked it's an element.
406
- const element: ReactElement = (child: any);
407
- const Component = element.type;
408
- if (__DEV__) {
409
- pushElementToDebugStack(element);
410
- }
411
- if (typeof Component !== 'function') {
412
- break;
413
- }
414
- processChild(element, Component);
415
- }
416
-
417
- // Extra closure so queue and replace can be captured properly
418
- function processChild(element, Component) {
419
- const isClass = shouldConstruct(Component);
420
- const publicContext = processContext(Component, context, threadID, isClass);
421
-
422
- let queue = [];
423
- let replace = false;
424
- const updater = {
425
- isMounted: function(publicInstance) {
426
- return false;
427
- },
428
- enqueueForceUpdate: function(publicInstance) {
429
- if (queue === null) {
430
- warnNoop(publicInstance, 'forceUpdate');
431
- return null;
432
- }
433
- },
434
- enqueueReplaceState: function(publicInstance, completeState) {
435
- replace = true;
436
- queue = [completeState];
437
- },
438
- enqueueSetState: function(publicInstance, currentPartialState) {
439
- if (queue === null) {
440
- warnNoop(publicInstance, 'setState');
441
- return null;
442
- }
443
- queue.push(currentPartialState);
444
- },
445
- };
446
-
447
- let inst;
448
- if (isClass) {
449
- inst = new Component(element.props, publicContext, updater);
450
-
451
- if (typeof Component.getDerivedStateFromProps === 'function') {
452
- if (__DEV__) {
453
- if (inst.state === null || inst.state === undefined) {
454
- const componentName =
455
- getComponentNameFromType(Component) || 'Unknown';
456
- if (!didWarnAboutUninitializedState[componentName]) {
457
- console.error(
458
- '`%s` uses `getDerivedStateFromProps` but its initial state is ' +
459
- '%s. This is not recommended. Instead, define the initial state by ' +
460
- 'assigning an object to `this.state` in the constructor of `%s`. ' +
461
- 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.',
462
- componentName,
463
- inst.state === null ? 'null' : 'undefined',
464
- componentName,
465
- );
466
- didWarnAboutUninitializedState[componentName] = true;
467
- }
468
- }
469
-
470
- // If new component APIs are defined, "unsafe" lifecycles won't be called.
471
- // Warn about these lifecycles if they are present.
472
- // Don't warn about react-lifecycles-compat polyfilled methods though.
473
- if (
474
- typeof Component.getDerivedStateFromProps === 'function' ||
475
- typeof inst.getSnapshotBeforeUpdate === 'function'
476
- ) {
477
- let foundWillMountName = null;
478
- let foundWillReceivePropsName = null;
479
- let foundWillUpdateName = null;
480
- if (
481
- typeof inst.componentWillMount === 'function' &&
482
- inst.componentWillMount.__suppressDeprecationWarning !== true
483
- ) {
484
- foundWillMountName = 'componentWillMount';
485
- } else if (typeof inst.UNSAFE_componentWillMount === 'function') {
486
- foundWillMountName = 'UNSAFE_componentWillMount';
487
- }
488
- if (
489
- typeof inst.componentWillReceiveProps === 'function' &&
490
- inst.componentWillReceiveProps.__suppressDeprecationWarning !==
491
- true
492
- ) {
493
- foundWillReceivePropsName = 'componentWillReceiveProps';
494
- } else if (
495
- typeof inst.UNSAFE_componentWillReceiveProps === 'function'
496
- ) {
497
- foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
498
- }
499
- if (
500
- typeof inst.componentWillUpdate === 'function' &&
501
- inst.componentWillUpdate.__suppressDeprecationWarning !== true
502
- ) {
503
- foundWillUpdateName = 'componentWillUpdate';
504
- } else if (typeof inst.UNSAFE_componentWillUpdate === 'function') {
505
- foundWillUpdateName = 'UNSAFE_componentWillUpdate';
506
- }
507
- if (
508
- foundWillMountName !== null ||
509
- foundWillReceivePropsName !== null ||
510
- foundWillUpdateName !== null
511
- ) {
512
- const componentName =
513
- getComponentNameFromType(Component) || 'Component';
514
- const newApiName =
515
- typeof Component.getDerivedStateFromProps === 'function'
516
- ? 'getDerivedStateFromProps()'
517
- : 'getSnapshotBeforeUpdate()';
518
- if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
519
- didWarnAboutLegacyLifecyclesAndDerivedState[
520
- componentName
521
- ] = true;
522
- console.error(
523
- 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
524
- '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
525
- 'The above lifecycles should be removed. Learn more about this warning here:\n' +
526
- 'https://reactjs.org/link/unsafe-component-lifecycles',
527
- componentName,
528
- newApiName,
529
- foundWillMountName !== null
530
- ? `\n ${foundWillMountName}`
531
- : '',
532
- foundWillReceivePropsName !== null
533
- ? `\n ${foundWillReceivePropsName}`
534
- : '',
535
- foundWillUpdateName !== null
536
- ? `\n ${foundWillUpdateName}`
537
- : '',
538
- );
539
- }
540
- }
541
- }
542
- }
543
-
544
- const partialState = Component.getDerivedStateFromProps.call(
545
- null,
546
- element.props,
547
- inst.state,
548
- );
549
-
550
- if (__DEV__) {
551
- if (partialState === undefined) {
552
- const componentName =
553
- getComponentNameFromType(Component) || 'Unknown';
554
- if (!didWarnAboutUndefinedDerivedState[componentName]) {
555
- console.error(
556
- '%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
557
- 'You have returned undefined.',
558
- componentName,
559
- );
560
- didWarnAboutUndefinedDerivedState[componentName] = true;
561
- }
562
- }
563
- }
564
-
565
- if (partialState != null) {
566
- inst.state = assign({}, inst.state, partialState);
567
- }
568
- }
569
- } else {
570
- if (__DEV__) {
571
- if (
572
- Component.prototype &&
573
- typeof Component.prototype.render === 'function'
574
- ) {
575
- const componentName =
576
- getComponentNameFromType(Component) || 'Unknown';
577
-
578
- if (!didWarnAboutBadClass[componentName]) {
579
- console.error(
580
- "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
581
- 'This is likely to cause errors. Change %s to extend React.Component instead.',
582
- componentName,
583
- componentName,
584
- );
585
- didWarnAboutBadClass[componentName] = true;
586
- }
587
- }
588
- }
589
- const componentIdentity = {};
590
- prepareToUseHooks(componentIdentity);
591
- inst = Component(element.props, publicContext, updater);
592
- inst = finishHooks(Component, element.props, inst, publicContext);
593
-
594
- if (__DEV__) {
595
- // Support for module components is deprecated and is removed behind a flag.
596
- // Whether or not it would crash later, we want to show a good message in DEV first.
597
- if (inst != null && inst.render != null) {
598
- const componentName =
599
- getComponentNameFromType(Component) || 'Unknown';
600
- if (!didWarnAboutModulePatternComponent[componentName]) {
601
- console.error(
602
- 'The <%s /> component appears to be a function component that returns a class instance. ' +
603
- 'Change %s to a class that extends React.Component instead. ' +
604
- "If you can't use a class try assigning the prototype on the function as a workaround. " +
605
- "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
606
- 'cannot be called with `new` by React.',
607
- componentName,
608
- componentName,
609
- componentName,
610
- );
611
- didWarnAboutModulePatternComponent[componentName] = true;
612
- }
613
- }
614
- }
615
-
616
- // If the flag is on, everything is assumed to be a function component.
617
- // Otherwise, we also do the unfortunate dynamic checks.
618
- if (
619
- disableModulePatternComponents ||
620
- inst == null ||
621
- inst.render == null
622
- ) {
623
- child = inst;
624
- return;
625
- }
626
- }
627
-
628
- inst.props = element.props;
629
- inst.context = publicContext;
630
- inst.updater = updater;
631
-
632
- let initialState = inst.state;
633
- if (initialState === undefined) {
634
- inst.state = initialState = null;
635
- }
636
- if (
637
- typeof inst.UNSAFE_componentWillMount === 'function' ||
638
- typeof inst.componentWillMount === 'function'
639
- ) {
640
- if (typeof inst.componentWillMount === 'function') {
641
- // In order to support react-lifecycles-compat polyfilled components,
642
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
643
- if (typeof Component.getDerivedStateFromProps !== 'function') {
644
- if (__DEV__) {
645
- if (
646
- warnAboutDeprecatedLifecycles &&
647
- inst.componentWillMount.__suppressDeprecationWarning !== true
648
- ) {
649
- const componentName =
650
- getComponentNameFromType(Component) || 'Unknown';
651
-
652
- if (!didWarnAboutDeprecatedWillMount[componentName]) {
653
- console.warn(
654
- // keep this warning in sync with ReactStrictModeWarning.js
655
- 'componentWillMount has been renamed, and is not recommended for use. ' +
656
- 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' +
657
- '* Move code from componentWillMount to componentDidMount (preferred in most cases) ' +
658
- 'or the constructor.\n' +
659
- '\nPlease update the following components: %s',
660
- componentName,
661
- );
662
- didWarnAboutDeprecatedWillMount[componentName] = true;
663
- }
664
- }
665
- }
666
-
667
- inst.componentWillMount();
668
- }
669
- }
670
- if (
671
- typeof inst.UNSAFE_componentWillMount === 'function' &&
672
- typeof Component.getDerivedStateFromProps !== 'function'
673
- ) {
674
- // In order to support react-lifecycles-compat polyfilled components,
675
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
676
- inst.UNSAFE_componentWillMount();
677
- }
678
- if (queue.length) {
679
- const oldQueue = queue;
680
- const oldReplace = replace;
681
- queue = null;
682
- replace = false;
683
-
684
- if (oldReplace && oldQueue.length === 1) {
685
- inst.state = oldQueue[0];
686
- } else {
687
- let nextState = oldReplace ? oldQueue[0] : inst.state;
688
- let dontMutate = true;
689
- for (let i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
690
- const partial = oldQueue[i];
691
- const partialState =
692
- typeof partial === 'function'
693
- ? partial.call(inst, nextState, element.props, publicContext)
694
- : partial;
695
- if (partialState != null) {
696
- if (dontMutate) {
697
- dontMutate = false;
698
- nextState = assign({}, nextState, partialState);
699
- } else {
700
- assign(nextState, partialState);
701
- }
702
- }
703
- }
704
- inst.state = nextState;
705
- }
706
- } else {
707
- queue = null;
708
- }
709
- }
710
- child = inst.render();
711
-
712
- let childContext;
713
- if (disableLegacyContext) {
714
- if (__DEV__) {
715
- const childContextTypes = Component.childContextTypes;
716
- if (childContextTypes !== undefined) {
717
- console.error(
718
- '%s uses the legacy childContextTypes API which is no longer supported. ' +
719
- 'Use React.createContext() instead.',
720
- getComponentNameFromType(Component) || 'Unknown',
721
- );
722
- }
723
- }
724
- } else {
725
- if (typeof inst.getChildContext === 'function') {
726
- const childContextTypes = Component.childContextTypes;
727
- if (typeof childContextTypes === 'object') {
728
- childContext = inst.getChildContext();
729
- for (const contextKey in childContext) {
730
- if (!(contextKey in childContextTypes)) {
731
- throw new Error(
732
- `${getComponentNameFromType(Component) ||
733
- 'Unknown'}.getChildContext(): key "${contextKey}" is not defined in childContextTypes.`,
734
- );
735
- }
736
- }
737
- } else {
738
- if (__DEV__) {
739
- console.error(
740
- '%s.getChildContext(): childContextTypes must be defined in order to ' +
741
- 'use getChildContext().',
742
- getComponentNameFromType(Component) || 'Unknown',
743
- );
744
- }
745
- }
746
- }
747
- if (childContext) {
748
- context = assign({}, context, childContext);
749
- }
750
- }
751
- }
752
- return {child, context};
753
-}
754
-
755
-type Frame = {
756
- type: mixed,
757
- domNamespace: string,
758
- children: FlatReactChildren,
759
- fallbackFrame?: Frame,
760
- childIndex: number,
761
- context: Object,
762
- footer: string,
763
- ...
764
-};
765
-
766
-type FrameDev = Frame & {|debugElementStack: Array<ReactElement>|};
767
-
768
-class ReactDOMServerRenderer {
769
- threadID: ThreadID;
770
- stack: Array<Frame>;
771
- exhausted: boolean;
772
- // TODO: type this more strictly:
773
- currentSelectValue: any;
774
- previousWasTextNode: boolean;
775
- makeStaticMarkup: boolean;
776
- suspenseDepth: number;
777
-
778
- contextIndex: number;
779
- contextStack: Array<ReactContext<any>>;
780
- contextValueStack: Array<any>;
781
- contextProviderStack: ?Array<ReactProvider<any>>; // DEV-only
782
-
783
- constructor(children: mixed, makeStaticMarkup: boolean) {
784
- const flatChildren = flattenTopLevelChildren(children);
785
-
786
- const topFrame: Frame = {
787
- type: null,
788
- // Assume all trees start in the HTML namespace (not totally true, but
789
- // this is what we did historically)
790
- domNamespace: HTML_NAMESPACE,
791
- children: flatChildren,
792
- childIndex: 0,
793
- context: emptyObject,
794
- footer: '',
795
- };
796
- if (__DEV__) {
797
- ((topFrame: any): FrameDev).debugElementStack = [];
798
- }
799
- this.threadID = allocThreadID();
800
- this.stack = [topFrame];
801
- this.exhausted = false;
802
- this.currentSelectValue = null;
803
- this.previousWasTextNode = false;
804
- this.makeStaticMarkup = makeStaticMarkup;
805
- this.suspenseDepth = 0;
806
-
807
- // Context (new API)
808
- this.contextIndex = -1;
809
- this.contextStack = [];
810
- this.contextValueStack = [];
811
-
812
- if (__DEV__) {
813
- this.contextProviderStack = [];
814
- }
815
- }
816
-
817
- destroy() {
818
- if (!this.exhausted) {
819
- this.exhausted = true;
820
- this.clearProviders();
821
- freeThreadID(this.threadID);
822
- }
823
- }
824
-
825
- /**
826
- * Note: We use just two stacks regardless of how many context providers you have.
827
- * Providers are always popped in the reverse order to how they were pushed
828
- * so we always know on the way down which provider you'll encounter next on the way up.
829
- * On the way down, we push the current provider, and its context value *before*
830
- * we mutated it, onto the stacks. Therefore, on the way up, we always know which
831
- * provider needs to be "restored" to which value.
832
- * https://github.com/facebook/react/pull/12985#issuecomment-396301248
833
- */
834
-
835
- pushProvider<T>(provider: ReactProvider<T>): void {
836
- const index = ++this.contextIndex;
837
- const context: ReactContext<any> = provider.type._context;
838
- const threadID = this.threadID;
839
- validateContextBounds(context, threadID);
840
- const previousValue = context[threadID];
841
-
842
- // Remember which value to restore this context to on our way up.
843
- this.contextStack[index] = context;
844
- this.contextValueStack[index] = previousValue;
845
- if (__DEV__) {
846
- // Only used for push/pop mismatch warnings.
847
- (this.contextProviderStack: any)[index] = provider;
848
- }
849
-
850
- // Mutate the current value.
851
- context[threadID] = provider.props.value;
852
- }
853
-
854
- popProvider<T>(provider: ReactProvider<T>): void {
855
- const index = this.contextIndex;
856
- if (__DEV__) {
857
- if (index < 0 || provider !== (this.contextProviderStack: any)[index]) {
858
- console.error('Unexpected pop.');
859
- }
860
- }
861
-
862
- const context: ReactContext<any> = this.contextStack[index];
863
- const previousValue = this.contextValueStack[index];
864
-
865
- // "Hide" these null assignments from Flow by using `any`
866
- // because conceptually they are deletions--as long as we
867
- // promise to never access values beyond `this.contextIndex`.
868
- this.contextStack[index] = (null: any);
869
- this.contextValueStack[index] = (null: any);
870
- if (__DEV__) {
871
- (this.contextProviderStack: any)[index] = (null: any);
872
- }
873
- this.contextIndex--;
874
-
875
- // Restore to the previous value we stored as we were walking down.
876
- // We've already verified that this context has been expanded to accommodate
877
- // this thread id, so we don't need to do it again.
878
- context[this.threadID] = previousValue;
879
- }
880
-
881
- clearProviders(): void {
882
- // Restore any remaining providers on the stack to previous values
883
- for (let index = this.contextIndex; index >= 0; index--) {
884
- const context: ReactContext<any> = this.contextStack[index];
885
- const previousValue = this.contextValueStack[index];
886
- context[this.threadID] = previousValue;
887
- }
888
- }
889
-
890
- read(bytes: number): string | null {
891
- if (this.exhausted) {
892
- return null;
893
- }
894
-
895
- const prevPartialRenderer = currentPartialRenderer;
896
- setCurrentPartialRenderer(this);
897
- const prevDispatcher = ReactCurrentDispatcher.current;
898
- ReactCurrentDispatcher.current = Dispatcher;
899
- try {
900
- // Markup generated within <Suspense> ends up buffered until we know
901
- // nothing in that boundary suspended
902
- const out = [''];
903
- let suspended = false;
904
- while (out[0].length < bytes) {
905
- if (this.stack.length === 0) {
906
- this.exhausted = true;
907
- freeThreadID(this.threadID);
908
- break;
909
- }
910
- const frame: Frame = this.stack[this.stack.length - 1];
911
- if (suspended || frame.childIndex >= frame.children.length) {
912
- const footer = frame.footer;
913
- if (footer !== '') {
914
- this.previousWasTextNode = false;
915
- }
916
- this.stack.pop();
917
- if (frame.type === 'select') {
918
- this.currentSelectValue = null;
919
- } else if (
920
- frame.type != null &&
921
- frame.type.type != null &&
922
- frame.type.type.$$typeof === REACT_PROVIDER_TYPE
923
- ) {
924
- const provider: ReactProvider<any> = (frame.type: any);
925
- this.popProvider(provider);
926
- } else if (frame.type === REACT_SUSPENSE_TYPE) {
927
- this.suspenseDepth--;
928
- const buffered = out.pop();
929
-
930
- if (suspended) {
931
- suspended = false;
932
- // If rendering was suspended at this boundary, render the fallbackFrame
933
- const fallbackFrame = frame.fallbackFrame;
934
-
935
- if (!fallbackFrame) {
936
- throw new Error(
937
- 'ReactDOMServer did not find an internal fallback frame for Suspense. ' +
938
- 'This is a bug in React. Please file an issue.',
939
- );
940
- }
941
-
942
- this.stack.push(fallbackFrame);
943
- out[this.suspenseDepth] += '<!--$!-->';
944
- // Skip flushing output since we're switching to the fallback
945
- continue;
946
- } else {
947
- out[this.suspenseDepth] += buffered;
948
- }
949
- }
950
-
951
- // Flush output
952
- out[this.suspenseDepth] += footer;
953
- continue;
954
- }
955
- const child = frame.children[frame.childIndex++];
956
-
957
- let outBuffer = '';
958
- if (__DEV__) {
959
- pushCurrentDebugStack(this.stack);
960
- // We're starting work on this frame, so reset its inner stack.
961
- ((frame: any): FrameDev).debugElementStack.length = 0;
962
- }
963
- try {
964
- outBuffer += this.render(child, frame.context, frame.domNamespace);
965
- } catch (err) {
966
- if (err != null && typeof err.then === 'function') {
967
- if (this.suspenseDepth <= 0) {
968
- throw new Error(
969
- // TODO: include component name. This is a bit tricky with current factoring.
970
- 'A React component suspended while rendering, but no fallback UI was specified.\n' +
971
- '\n' +
972
- 'Add a <Suspense fallback=...> component higher in the tree to ' +
973
- 'provide a loading indicator or placeholder to display.',
974
- );
975
- }
976
-
977
- suspended = true;
978
- } else {
979
- throw err;
980
- }
981
- } finally {
982
- if (__DEV__) {
983
- popCurrentDebugStack();
984
- }
985
- }
986
- if (out.length <= this.suspenseDepth) {
987
- out.push('');
988
- }
989
- out[this.suspenseDepth] += outBuffer;
990
- }
991
- return out[0];
992
- } finally {
993
- ReactCurrentDispatcher.current = prevDispatcher;
994
- setCurrentPartialRenderer(prevPartialRenderer);
995
- resetHooksState();
996
- }
997
- }
998
-
999
- render(
1000
- child: ReactNode | null,
1001
- context: Object,
1002
- parentNamespace: string,
1003
- ): string {
1004
- if (typeof child === 'string' || typeof child === 'number') {
1005
- const text = '' + child;
1006
- if (text === '') {
1007
- return '';
1008
- }
1009
- if (this.makeStaticMarkup) {
1010
- return escapeTextForBrowser(text);
1011
- }
1012
- if (this.previousWasTextNode) {
1013
- return '<!-- -->' + escapeTextForBrowser(text);
1014
- }
1015
- this.previousWasTextNode = true;
1016
- return escapeTextForBrowser(text);
1017
- } else {
1018
- let nextChild;
1019
- ({child: nextChild, context} = resolve(child, context, this.threadID));
1020
- if (nextChild === null || nextChild === false) {
1021
- return '';
1022
- } else if (!React.isValidElement(nextChild)) {
1023
- if (nextChild != null && nextChild.$$typeof != null) {
1024
- // Catch unexpected special types early.
1025
- const $$typeof = nextChild.$$typeof;
1026
-
1027
- if ($$typeof === REACT_PORTAL_TYPE) {
1028
- throw new Error(
1029
- 'Portals are not currently supported by the server renderer. ' +
1030
- 'Render them conditionally so that they only appear on the client render.',
1031
- );
1032
- }
1033
-
1034
- // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type.
1035
- throw new Error(
1036
- `Unknown element-like object type: ${($$typeof: any).toString()}. This is likely a bug in React. ` +
1037
- 'Please file an issue.',
1038
- );
1039
- }
1040
- const nextChildren = toArray(nextChild);
1041
- const frame: Frame = {
1042
- type: null,
1043
- domNamespace: parentNamespace,
1044
- children: nextChildren,
1045
- childIndex: 0,
1046
- context: context,
1047
- footer: '',
1048
- };
1049
- if (__DEV__) {
1050
- ((frame: any): FrameDev).debugElementStack = [];
1051
- }
1052
- this.stack.push(frame);
1053
- return '';
1054
- }
1055
- // Safe because we just checked it's an element.
1056
- const nextElement = ((nextChild: any): ReactElement);
1057
- const elementType = nextElement.type;
1058
-
1059
- if (typeof elementType === 'string') {
1060
- return this.renderDOM(nextElement, context, parentNamespace);
1061
- }
1062
-
1063
- switch (elementType) {
1064
- // TODO: LegacyHidden acts the same as a fragment. This only works
1065
- // because we currently assume that every instance of LegacyHidden is
1066
- // accompanied by a host component wrapper. In the hidden mode, the host
1067
- // component is given a `hidden` attribute, which ensures that the
1068
- // initial HTML is not visible. To support the use of LegacyHidden as a
1069
- // true fragment, without an extra DOM node, we would have to hide the
1070
- // initial HTML in some other way.
1071
- case REACT_LEGACY_HIDDEN_TYPE:
1072
- case REACT_DEBUG_TRACING_MODE_TYPE:
1073
- case REACT_STRICT_MODE_TYPE:
1074
- case REACT_PROFILER_TYPE:
1075
- case REACT_SUSPENSE_LIST_TYPE:
1076
- case REACT_FRAGMENT_TYPE: {
1077
- const nextChildren = toArray(
1078
- ((nextChild: any): ReactElement).props.children,
1079
- );
1080
- const frame: Frame = {
1081
- type: null,
1082
- domNamespace: parentNamespace,
1083
- children: nextChildren,
1084
- childIndex: 0,
1085
- context: context,
1086
- footer: '',
1087
- };
1088
- if (__DEV__) {
1089
- ((frame: any): FrameDev).debugElementStack = [];
1090
- }
1091
- this.stack.push(frame);
1092
- return '';
1093
- }
1094
- case REACT_SUSPENSE_TYPE: {
1095
- const fallback = ((nextChild: any): ReactElement).props.fallback;
1096
- const fallbackChildren = toArray(fallback);
1097
- const nextChildren = toArray(
1098
- ((nextChild: any): ReactElement).props.children,
1099
- );
1100
- const fallbackFrame: Frame = {
1101
- type: null,
1102
- domNamespace: parentNamespace,
1103
- children: fallbackChildren,
1104
- childIndex: 0,
1105
- context: context,
1106
- footer: '<!--/$-->',
1107
- };
1108
- const frame: Frame = {
1109
- fallbackFrame,
1110
- type: REACT_SUSPENSE_TYPE,
1111
- domNamespace: parentNamespace,
1112
- children: nextChildren,
1113
- childIndex: 0,
1114
- context: context,
1115
- footer: '<!--/$-->',
1116
- };
1117
- if (__DEV__) {
1118
- ((frame: any): FrameDev).debugElementStack = [];
1119
- ((fallbackFrame: any): FrameDev).debugElementStack = [];
1120
- }
1121
- this.stack.push(frame);
1122
- this.suspenseDepth++;
1123
- return '<!--$-->';
1124
- }
1125
- // eslint-disable-next-line-no-fallthrough
1126
- case REACT_SCOPE_TYPE: {
1127
- if (enableScopeAPI) {
1128
- const nextChildren = toArray(
1129
- ((nextChild: any): ReactElement).props.children,
1130
- );
1131
- const frame: Frame = {
1132
- type: null,
1133
- domNamespace: parentNamespace,
1134
- children: nextChildren,
1135
- childIndex: 0,
1136
- context: context,
1137
- footer: '',
1138
- };
1139
- if (__DEV__) {
1140
- ((frame: any): FrameDev).debugElementStack = [];
1141
- }
1142
- this.stack.push(frame);
1143
- return '';
1144
- }
1145
- throw new Error(
1146
- 'ReactDOMServer does not yet support scope components.',
1147
- );
1148
- }
1149
- // eslint-disable-next-line-no-fallthrough
1150
- default:
1151
- break;
1152
- }
1153
- if (typeof elementType === 'object' && elementType !== null) {
1154
- switch (elementType.$$typeof) {
1155
- case REACT_FORWARD_REF_TYPE: {
1156
- const element: ReactElement = ((nextChild: any): ReactElement);
1157
- let nextChildren;
1158
- const componentIdentity = {};
1159
- prepareToUseHooks(componentIdentity);
1160
- nextChildren = elementType.render(element.props, element.ref);
1161
- nextChildren = finishHooks(
1162
- elementType.render,
1163
- element.props,
1164
- nextChildren,
1165
- element.ref,
1166
- );
1167
- nextChildren = toArray(nextChildren);
1168
- const frame: Frame = {
1169
- type: null,
1170
- domNamespace: parentNamespace,
1171
- children: nextChildren,
1172
- childIndex: 0,
1173
- context: context,
1174
- footer: '',
1175
- };
1176
- if (__DEV__) {
1177
- ((frame: any): FrameDev).debugElementStack = [];
1178
- }
1179
- this.stack.push(frame);
1180
- return '';
1181
- }
1182
- case REACT_MEMO_TYPE: {
1183
- const element: ReactElement = ((nextChild: any): ReactElement);
1184
- const nextChildren = [
1185
- React.createElement(
1186
- elementType.type,
1187
- assign({ref: element.ref}, element.props),
1188
- ),
1189
- ];
1190
- const frame: Frame = {
1191
- type: null,
1192
- domNamespace: parentNamespace,
1193
- children: nextChildren,
1194
- childIndex: 0,
1195
- context: context,
1196
- footer: '',
1197
- };
1198
- if (__DEV__) {
1199
- ((frame: any): FrameDev).debugElementStack = [];
1200
- }
1201
- this.stack.push(frame);
1202
- return '';
1203
- }
1204
- case REACT_PROVIDER_TYPE: {
1205
- const provider: ReactProvider<any> = (nextChild: any);
1206
- const nextProps = provider.props;
1207
- const nextChildren = toArray(nextProps.children);
1208
- const frame: Frame = {
1209
- type: provider,
1210
- domNamespace: parentNamespace,
1211
- children: nextChildren,
1212
- childIndex: 0,
1213
- context: context,
1214
- footer: '',
1215
- };
1216
- if (__DEV__) {
1217
- ((frame: any): FrameDev).debugElementStack = [];
1218
- }
1219
-
1220
- this.pushProvider(provider);
1221
-
1222
- this.stack.push(frame);
1223
- return '';
1224
- }
1225
- case REACT_CONTEXT_TYPE: {
1226
- let reactContext = (nextChild: any).type;
1227
- // The logic below for Context differs depending on PROD or DEV mode. In
1228
- // DEV mode, we create a separate object for Context.Consumer that acts
1229
- // like a proxy to Context. This proxy object adds unnecessary code in PROD
1230
- // so we use the old behaviour (Context.Consumer references Context) to
1231
- // reduce size and overhead. The separate object references context via
1232
- // a property called "_context", which also gives us the ability to check
1233
- // in DEV mode if this property exists or not and warn if it does not.
1234
- if (__DEV__) {
1235
- if ((reactContext: any)._context === undefined) {
1236
- // This may be because it's a Context (rather than a Consumer).
1237
- // Or it may be because it's older React where they're the same thing.
1238
- // We only want to warn if we're sure it's a new React.
1239
- if (reactContext !== reactContext.Consumer) {
1240
- if (!hasWarnedAboutUsingContextAsConsumer) {
1241
- hasWarnedAboutUsingContextAsConsumer = true;
1242
- console.error(
1243
- 'Rendering <Context> directly is not supported and will be removed in ' +
1244
- 'a future major release. Did you mean to render <Context.Consumer> instead?',
1245
- );
1246
- }
1247
- }
1248
- } else {
1249
- reactContext = (reactContext: any)._context;
1250
- }
1251
- }
1252
- const nextProps: any = (nextChild: any).props;
1253
- const threadID = this.threadID;
1254
- validateContextBounds(reactContext, threadID);
1255
- const nextValue = reactContext[threadID];
1256
-
1257
- const nextChildren = toArray(nextProps.children(nextValue));
1258
- const frame: Frame = {
1259
- type: nextChild,
1260
- domNamespace: parentNamespace,
1261
- children: nextChildren,
1262
- childIndex: 0,
1263
- context: context,
1264
- footer: '',
1265
- };
1266
- if (__DEV__) {
1267
- ((frame: any): FrameDev).debugElementStack = [];
1268
- }
1269
- this.stack.push(frame);
1270
- return '';
1271
- }
1272
- // eslint-disable-next-line-no-fallthrough
1273
- case REACT_LAZY_TYPE: {
1274
- const element: ReactElement = (nextChild: any);
1275
- const lazyComponent: LazyComponent<any, any> = (nextChild: any)
1276
- .type;
1277
- // Attempt to initialize lazy component regardless of whether the
1278
- // suspense server-side renderer is enabled so synchronously
1279
- // resolved constructors are supported.
1280
- const payload = lazyComponent._payload;
1281
- const init = lazyComponent._init;
1282
- const result = init(payload);
1283
- const nextChildren = [
1284
- React.createElement(
1285
- result,
1286
- assign({ref: element.ref}, element.props),
1287
- ),
1288
- ];
1289
- const frame: Frame = {
1290
- type: null,
1291
- domNamespace: parentNamespace,
1292
- children: nextChildren,
1293
- childIndex: 0,
1294
- context: context,
1295
- footer: '',
1296
- };
1297
- if (__DEV__) {
1298
- ((frame: any): FrameDev).debugElementStack = [];
1299
- }
1300
- this.stack.push(frame);
1301
- return '';
1302
- }
1303
- }
1304
- }
1305
-
1306
- let info = '';
1307
- if (__DEV__) {
1308
- const owner = nextElement._owner;
1309
- if (
1310
- elementType === undefined ||
1311
- (typeof elementType === 'object' &&
1312
- elementType !== null &&
1313
- Object.keys(elementType).length === 0)
1314
- ) {
1315
- info +=
1316
- ' You likely forgot to export your component from the file ' +
1317
- "it's defined in, or you might have mixed up default and " +
1318
- 'named imports.';
1319
- }
1320
- const ownerName = owner ? getComponentNameFromType(owner) : null;
1321
- if (ownerName) {
1322
- info += '\n\nCheck the render method of `' + ownerName + '`.';
1323
- }
1324
- }
1325
-
1326
- throw new Error(
1327
- 'Element type is invalid: expected a string (for built-in ' +
1328
- 'components) or a class/function (for composite components) ' +
1329
- `but got: ${
1330
- elementType == null ? elementType : typeof elementType
1331
- }.${info}`,
1332
- );
1333
- }
1334
- }
1335
-
1336
- renderDOM(
1337
- element: ReactElement,
1338
- context: Object,
1339
- parentNamespace: string,
1340
- ): string {
1341
- const tag = element.type;
1342
-
1343
- let namespace = parentNamespace;
1344
- if (parentNamespace === HTML_NAMESPACE) {
1345
- namespace = getIntrinsicNamespace(tag);
1346
- }
1347
-
1348
- let props = element.props;
1349
-
1350
- if (__DEV__) {
1351
- if (namespace === HTML_NAMESPACE) {
1352
- const isCustomComponent = isCustomComponentFn(tag, props);
1353
- // Should this check be gated by parent namespace? Not sure we want to
1354
- // allow <SVG> or <mATH>.
1355
- if (!isCustomComponent && tag.toLowerCase() !== element.type) {
1356
- console.error(
1357
- '<%s /> is using incorrect casing. ' +
1358
- 'Use PascalCase for React components, ' +
1359
- 'or lowercase for HTML elements.',
1360
- element.type,
1361
- );
1362
- }
1363
- }
1364
- }
1365
-
1366
- validateDangerousTag(tag);
1367
-
1368
- if (tag === 'input') {
1369
- if (__DEV__) {
1370
- checkControlledValueProps('input', props);
1371
-
1372
- if (
1373
- props.checked !== undefined &&
1374
- props.defaultChecked !== undefined &&
1375
- !didWarnDefaultChecked
1376
- ) {
1377
- console.error(
1378
- '%s contains an input of type %s with both checked and defaultChecked props. ' +
1379
- 'Input elements must be either controlled or uncontrolled ' +
1380
- '(specify either the checked prop, or the defaultChecked prop, but not ' +
1381
- 'both). Decide between using a controlled or uncontrolled input ' +
1382
- 'element and remove one of these props. More info: ' +
1383
- 'https://reactjs.org/link/controlled-components',
1384
- 'A component',
1385
- props.type,
1386
- );
1387
- didWarnDefaultChecked = true;
1388
- }
1389
- if (
1390
- props.value !== undefined &&
1391
- props.defaultValue !== undefined &&
1392
- !didWarnDefaultInputValue
1393
- ) {
1394
- console.error(
1395
- '%s contains an input of type %s with both value and defaultValue props. ' +
1396
- 'Input elements must be either controlled or uncontrolled ' +
1397
- '(specify either the value prop, or the defaultValue prop, but not ' +
1398
- 'both). Decide between using a controlled or uncontrolled input ' +
1399
- 'element and remove one of these props. More info: ' +
1400
- 'https://reactjs.org/link/controlled-components',
1401
- 'A component',
1402
- props.type,
1403
- );
1404
- didWarnDefaultInputValue = true;
1405
- }
1406
- }
1407
-
1408
- props = assign(
1409
- {
1410
- type: undefined,
1411
- },
1412
- props,
1413
- {
1414
- defaultChecked: undefined,
1415
- defaultValue: undefined,
1416
- value: props.value != null ? props.value : props.defaultValue,
1417
- checked: props.checked != null ? props.checked : props.defaultChecked,
1418
- },
1419
- );
1420
- } else if (tag === 'textarea') {
1421
- if (__DEV__) {
1422
- checkControlledValueProps('textarea', props);
1423
- if (
1424
- props.value !== undefined &&
1425
- props.defaultValue !== undefined &&
1426
- !didWarnDefaultTextareaValue
1427
- ) {
1428
- console.error(
1429
- 'Textarea elements must be either controlled or uncontrolled ' +
1430
- '(specify either the value prop, or the defaultValue prop, but not ' +
1431
- 'both). Decide between using a controlled or uncontrolled textarea ' +
1432
- 'and remove one of these props. More info: ' +
1433
- 'https://reactjs.org/link/controlled-components',
1434
- );
1435
- didWarnDefaultTextareaValue = true;
1436
- }
1437
- }
1438
-
1439
- let initialValue = props.value;
1440
- if (initialValue == null) {
1441
- let defaultValue = props.defaultValue;
1442
- // TODO (yungsters): Remove support for children content in <textarea>.
1443
- let textareaChildren = props.children;
1444
- if (textareaChildren != null) {
1445
- if (__DEV__) {
1446
- console.error(
1447
- 'Use the `defaultValue` or `value` props instead of setting ' +
1448
- 'children on <textarea>.',
1449
- );
1450
- }
1451
-
1452
- if (defaultValue != null) {
1453
- throw new Error(
1454
- 'If you supply `defaultValue` on a <textarea>, do not pass children.',
1455
- );
1456
- }
1457
-
1458
- if (isArray(textareaChildren)) {
1459
- if (textareaChildren.length > 1) {
1460
- throw new Error('<textarea> can only have at most one child.');
1461
- }
1462
-
1463
- textareaChildren = textareaChildren[0];
1464
- }
1465
-
1466
- if (__DEV__) {
1467
- checkPropStringCoercion(textareaChildren, 'children');
1468
- }
1469
- defaultValue = '' + textareaChildren;
1470
- }
1471
- if (defaultValue == null) {
1472
- defaultValue = '';
1473
- }
1474
- initialValue = defaultValue;
1475
- }
1476
-
1477
- if (__DEV__) {
1478
- checkFormFieldValueStringCoercion(initialValue);
1479
- }
1480
- props = assign({}, props, {
1481
- value: undefined,
1482
- children: '' + initialValue,
1483
- });
1484
- } else if (tag === 'select') {
1485
- if (__DEV__) {
1486
- checkControlledValueProps('select', props);
1487
-
1488
- for (let i = 0; i < valuePropNames.length; i++) {
1489
- const propName = valuePropNames[i];
1490
- if (props[propName] == null) {
1491
- continue;
1492
- }
1493
- const propNameIsArray = isArray(props[propName]);
1494
- if (props.multiple && !propNameIsArray) {
1495
- console.error(
1496
- 'The `%s` prop supplied to <select> must be an array if ' +
1497
- '`multiple` is true.',
1498
- propName,
1499
- );
1500
- } else if (!props.multiple && propNameIsArray) {
1501
- console.error(
1502
- 'The `%s` prop supplied to <select> must be a scalar ' +
1503
- 'value if `multiple` is false.',
1504
- propName,
1505
- );
1506
- }
1507
- }
1508
-
1509
- if (
1510
- props.value !== undefined &&
1511
- props.defaultValue !== undefined &&
1512
- !didWarnDefaultSelectValue
1513
- ) {
1514
- console.error(
1515
- 'Select elements must be either controlled or uncontrolled ' +
1516
- '(specify either the value prop, or the defaultValue prop, but not ' +
1517
- 'both). Decide between using a controlled or uncontrolled select ' +
1518
- 'element and remove one of these props. More info: ' +
1519
- 'https://reactjs.org/link/controlled-components',
1520
- );
1521
- didWarnDefaultSelectValue = true;
1522
- }
1523
- }
1524
- this.currentSelectValue =
1525
- props.value != null ? props.value : props.defaultValue;
1526
- props = assign({}, props, {
1527
- value: undefined,
1528
- });
1529
- } else if (tag === 'option') {
1530
- let selected = null;
1531
- const selectValue = this.currentSelectValue;
1532
- if (selectValue != null) {
1533
- let value;
1534
- if (props.value != null) {
1535
- if (__DEV__) {
1536
- checkFormFieldValueStringCoercion(props.value);
1537
- }
1538
- value = props.value + '';
1539
- } else {
1540
- if (__DEV__) {
1541
- if (props.dangerouslySetInnerHTML != null) {
1542
- if (!didWarnInvalidOptionInnerHTML) {
1543
- didWarnInvalidOptionInnerHTML = true;
1544
- console.error(
1545
- 'Pass a `value` prop if you set dangerouslyInnerHTML so React knows ' +
1546
- 'which value should be selected.',
1547
- );
1548
- }
1549
- }
1550
- }
1551
- value = flattenOptionChildren(props.children);
1552
- }
1553
- selected = false;
1554
- if (isArray(selectValue)) {
1555
- // multiple
1556
- for (let j = 0; j < selectValue.length; j++) {
1557
- if (__DEV__) {
1558
- checkFormFieldValueStringCoercion(selectValue[j]);
1559
- }
1560
- if ('' + selectValue[j] === value) {
1561
- selected = true;
1562
- break;
1563
- }
1564
- }
1565
- } else {
1566
- if (__DEV__) {
1567
- checkFormFieldValueStringCoercion(selectValue);
1568
- }
1569
- selected = '' + selectValue === value;
1570
- }
1571
-
1572
- props = assign(
1573
- {
1574
- selected: undefined,
1575
- },
1576
- props,
1577
- {
1578
- selected: selected,
1579
- },
1580
- );
1581
- }
1582
- }
1583
-
1584
- if (__DEV__) {
1585
- validatePropertiesInDevelopment(tag, props);
1586
- }
1587
-
1588
- assertValidProps(tag, props);
1589
-
1590
- let out = createOpenTagMarkup(
1591
- element.type,
1592
- tag,
1593
- props,
1594
- namespace,
1595
- this.makeStaticMarkup,
1596
- this.stack.length === 1,
1597
- );
1598
- let footer = '';
1599
- if (omittedCloseTags.hasOwnProperty(tag)) {
1600
- out += '/>';
1601
- } else {
1602
- out += '>';
1603
- footer = '</' + element.type + '>';
1604
- }
1605
- let children;
1606
- const innerMarkup = getNonChildrenInnerMarkup(props);
1607
- if (innerMarkup != null) {
1608
- children = [];
1609
- if (
1610
- newlineEatingTags.hasOwnProperty(tag) &&
1611
- innerMarkup.charAt(0) === '\n'
1612
- ) {
1613
- // text/html ignores the first character in these tags if it's a newline
1614
- // Prefer to break application/xml over text/html (for now) by adding
1615
- // a newline specifically to get eaten by the parser. (Alternately for
1616
- // textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first
1617
- // \r is normalized out by HTMLTextAreaElement#value.)
1618
- // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
1619
- // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
1620
- // See: <http://www.w3.org/TR/html5/syntax.html#newlines>
1621
- // See: Parsing of "textarea" "listing" and "pre" elements
1622
- // from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
1623
- out += '\n';
1624
- }
1625
- out += innerMarkup;
1626
- } else {
1627
- children = toArray(props.children);
1628
- }
1629
- const frame = {
1630
- domNamespace: getChildNamespace(parentNamespace, element.type),
1631
- type: tag,
1632
- children,
1633
- childIndex: 0,
1634
- context: context,
1635
- footer: footer,
1636
- };
1637
- if (__DEV__) {
1638
- ((frame: any): FrameDev).debugElementStack = [];
1639
- }
1640
- this.stack.push(frame);
1641
- this.previousWasTextNode = false;
1642
- return out;
1643
- }
1644
-}
1645
-
1646
-export default ReactDOMServerRenderer;
packages/react-dom/src/server/ReactPartialRendererContext.js
deleted
-160
@@ -1,160 +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
- *
7
- * @flow
8
- */
9
-
10
-import type {ThreadID} from './ReactThreadIDAllocator';
11
-import type {ReactContext} from 'shared/ReactTypes';
12
-
13
-import {disableLegacyContext} from 'shared/ReactFeatureFlags';
14
-import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
15
-import getComponentNameFromType from 'shared/getComponentNameFromType';
16
-import checkPropTypes from 'shared/checkPropTypes';
17
-
18
-let didWarnAboutInvalidateContextType;
19
-if (__DEV__) {
20
- didWarnAboutInvalidateContextType = new Set();
21
-}
22
-
23
-export const emptyObject = {};
24
-if (__DEV__) {
25
- Object.freeze(emptyObject);
26
-}
27
-
28
-function maskContext(type, context) {
29
- const contextTypes = type.contextTypes;
30
- if (!contextTypes) {
31
- return emptyObject;
32
- }
33
- const maskedContext = {};
34
- for (const contextName in contextTypes) {
35
- maskedContext[contextName] = context[contextName];
36
- }
37
- return maskedContext;
38
-}
39
-
40
-function checkContextTypes(typeSpecs, values, location: string) {
41
- if (__DEV__) {
42
- checkPropTypes(typeSpecs, values, location, 'Component');
43
- }
44
-}
45
-
46
-export function validateContextBounds(
47
- context: ReactContext<any>,
48
- threadID: ThreadID,
49
-) {
50
- // If we don't have enough slots in this context to store this threadID,
51
- // fill it in without leaving any holes to ensure that the VM optimizes
52
- // this as non-holey index properties.
53
- // (Note: If `react` package is < 16.6, _threadCount is undefined.)
54
- for (let i = context._threadCount | 0; i <= threadID; i++) {
55
- // We assume that this is the same as the defaultValue which might not be
56
- // true if we're rendering inside a secondary renderer but they are
57
- // secondary because these use cases are very rare.
58
- context[i] = context._currentValue2;
59
- context._threadCount = i + 1;
60
- }
61
-}
62
-
63
-export function processContext(
64
- type: Function,
65
- context: Object,
66
- threadID: ThreadID,
67
- isClass: boolean,
68
-) {
69
- if (isClass) {
70
- const contextType = type.contextType;
71
- if (__DEV__) {
72
- if ('contextType' in (type: any)) {
73
- const isValid =
74
- // Allow null for conditional declaration
75
- contextType === null ||
76
- (contextType !== undefined &&
77
- contextType.$$typeof === REACT_CONTEXT_TYPE &&
78
- contextType._context === undefined); // Not a <Context.Consumer>
79
-
80
- if (!isValid && !didWarnAboutInvalidateContextType.has(type)) {
81
- didWarnAboutInvalidateContextType.add(type);
82
-
83
- let addendum = '';
84
- if (contextType === undefined) {
85
- addendum =
86
- ' However, it is set to undefined. ' +
87
- 'This can be caused by a typo or by mixing up named and default imports. ' +
88
- 'This can also happen due to a circular dependency, so ' +
89
- 'try moving the createContext() call to a separate file.';
90
- } else if (typeof contextType !== 'object') {
91
- addendum = ' However, it is set to a ' + typeof contextType + '.';
92
- } else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
93
- addendum =
94
- ' Did you accidentally pass the Context.Provider instead?';
95
- } else if (contextType._context !== undefined) {
96
- // <Context.Consumer>
97
- addendum =
98
- ' Did you accidentally pass the Context.Consumer instead?';
99
- } else {
100
- addendum =
101
- ' However, it is set to an object with keys {' +
102
- Object.keys(contextType).join(', ') +
103
- '}.';
104
- }
105
- console.error(
106
- '%s defines an invalid contextType. ' +
107
- 'contextType should point to the Context object returned by React.createContext().%s',
108
- getComponentNameFromType(type) || 'Component',
109
- addendum,
110
- );
111
- }
112
- }
113
- }
114
- if (typeof contextType === 'object' && contextType !== null) {
115
- validateContextBounds(contextType, threadID);
116
- return contextType[threadID];
117
- }
118
- if (disableLegacyContext) {
119
- if (__DEV__) {
120
- if (type.contextTypes) {
121
- console.error(
122
- '%s uses the legacy contextTypes API which is no longer supported. ' +
123
- 'Use React.createContext() with static contextType instead.',
124
- getComponentNameFromType(type) || 'Unknown',
125
- );
126
- }
127
- }
128
- return emptyObject;
129
- } else {
130
- const maskedContext = maskContext(type, context);
131
- if (__DEV__) {
132
- if (type.contextTypes) {
133
- checkContextTypes(type.contextTypes, maskedContext, 'context');
134
- }
135
- }
136
- return maskedContext;
137
- }
138
- } else {
139
- if (disableLegacyContext) {
140
- if (__DEV__) {
141
- if (type.contextTypes) {
142
- console.error(
143
- '%s uses the legacy contextTypes API which is no longer supported. ' +
144
- 'Use React.createContext() with React.useContext() instead.',
145
- getComponentNameFromType(type) || 'Unknown',
146
- );
147
- }
148
- }
149
- return undefined;
150
- } else {
151
- const maskedContext = maskContext(type, context);
152
- if (__DEV__) {
153
- if (type.contextTypes) {
154
- checkContextTypes(type.contextTypes, maskedContext, 'context');
155
- }
156
- }
157
- return maskedContext;
158
- }
159
- }
160
-}
packages/react-dom/src/server/ReactPartialRendererHooks.js
deleted
-555
@@ -1,555 +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
- *
7
- * @flow
8
- */
9
-
10
-import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes';
11
-
12
-import type {
13
- MutableSource,
14
- MutableSourceGetSnapshotFn,
15
- MutableSourceSubscribeFn,
16
- ReactContext,
17
-} from 'shared/ReactTypes';
18
-import type PartialRenderer from './ReactPartialRenderer';
19
-
20
-import {validateContextBounds} from './ReactPartialRendererContext';
21
-
22
-import {enableCache} from 'shared/ReactFeatureFlags';
23
-import is from 'shared/objectIs';
24
-
25
-type BasicStateAction<S> = (S => S) | S;
26
-type Dispatch<A> = A => void;
27
-
28
-type Update<A> = {|
29
- action: A,
30
- next: Update<A> | null,
31
-|};
32
-
33
-type UpdateQueue<A> = {|
34
- last: Update<A> | null,
35
- dispatch: any,
36
-|};
37
-
38
-type Hook = {|
39
- memoizedState: any,
40
- queue: UpdateQueue<any> | null,
41
- next: Hook | null,
42
-|};
43
-
44
-let currentlyRenderingComponent: Object | null = null;
45
-let firstWorkInProgressHook: Hook | null = null;
46
-let workInProgressHook: Hook | null = null;
47
-// Whether the work-in-progress hook is a re-rendered hook
48
-let isReRender: boolean = false;
49
-// Whether an update was scheduled during the currently executing render pass.
50
-let didScheduleRenderPhaseUpdate: boolean = false;
51
-// Lazily created map of render-phase updates
52
-let renderPhaseUpdates: Map<UpdateQueue<any>, Update<any>> | null = null;
53
-// Counter to prevent infinite loops.
54
-let numberOfReRenders: number = 0;
55
-const RE_RENDER_LIMIT = 25;
56
-
57
-let isInHookUserCodeInDev = false;
58
-
59
-// In DEV, this is the name of the currently executing primitive hook
60
-let currentHookNameInDev: ?string;
61
-
62
-function resolveCurrentlyRenderingComponent(): Object {
63
- if (currentlyRenderingComponent === null) {
64
- throw new Error(
65
- 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
66
- ' one of the following reasons:\n' +
67
- '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
68
- '2. You might be breaking the Rules of Hooks\n' +
69
- '3. You might have more than one copy of React in the same app\n' +
70
- 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
71
- );
72
- }
73
-
74
- if (__DEV__) {
75
- if (isInHookUserCodeInDev) {
76
- console.error(
77
- 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' +
78
- 'You can only call Hooks at the top level of your React function. ' +
79
- 'For more information, see ' +
80
- 'https://reactjs.org/link/rules-of-hooks',
81
- );
82
- }
83
- }
84
- return currentlyRenderingComponent;
85
-}
86
-
87
-function areHookInputsEqual(
88
- nextDeps: Array<mixed>,
89
- prevDeps: Array<mixed> | null,
90
-) {
91
- if (prevDeps === null) {
92
- if (__DEV__) {
93
- console.error(
94
- '%s received a final argument during this render, but not during ' +
95
- 'the previous render. Even though the final argument is optional, ' +
96
- 'its type cannot change between renders.',
97
- currentHookNameInDev,
98
- );
99
- }
100
- return false;
101
- }
102
-
103
- if (__DEV__) {
104
- // Don't bother comparing lengths in prod because these arrays should be
105
- // passed inline.
106
- if (nextDeps.length !== prevDeps.length) {
107
- console.error(
108
- 'The final argument passed to %s changed size between renders. The ' +
109
- 'order and size of this array must remain constant.\n\n' +
110
- 'Previous: %s\n' +
111
- 'Incoming: %s',
112
- currentHookNameInDev,
113
- `[${nextDeps.join(', ')}]`,
114
- `[${prevDeps.join(', ')}]`,
115
- );
116
- }
117
- }
118
- for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
119
- if (is(nextDeps[i], prevDeps[i])) {
120
- continue;
121
- }
122
- return false;
123
- }
124
- return true;
125
-}
126
-
127
-function createHook(): Hook {
128
- if (numberOfReRenders > 0) {
129
- throw new Error('Rendered more hooks than during the previous render');
130
- }
131
- return {
132
- memoizedState: null,
133
- queue: null,
134
- next: null,
135
- };
136
-}
137
-
138
-function createWorkInProgressHook(): Hook {
139
- if (workInProgressHook === null) {
140
- // This is the first hook in the list
141
- if (firstWorkInProgressHook === null) {
142
- isReRender = false;
143
- firstWorkInProgressHook = workInProgressHook = createHook();
144
- } else {
145
- // There's already a work-in-progress. Reuse it.
146
- isReRender = true;
147
- workInProgressHook = firstWorkInProgressHook;
148
- }
149
- } else {
150
- if (workInProgressHook.next === null) {
151
- isReRender = false;
152
- // Append to the end of the list
153
- workInProgressHook = workInProgressHook.next = createHook();
154
- } else {
155
- // There's already a work-in-progress. Reuse it.
156
- isReRender = true;
157
- workInProgressHook = workInProgressHook.next;
158
- }
159
- }
160
- return workInProgressHook;
161
-}
162
-
163
-export function prepareToUseHooks(componentIdentity: Object): void {
164
- currentlyRenderingComponent = componentIdentity;
165
- if (__DEV__) {
166
- isInHookUserCodeInDev = false;
167
- }
168
-
169
- // The following should have already been reset
170
- // didScheduleRenderPhaseUpdate = false;
171
- // firstWorkInProgressHook = null;
172
- // numberOfReRenders = 0;
173
- // renderPhaseUpdates = null;
174
- // workInProgressHook = null;
175
-}
176
-
177
-export function finishHooks(
178
- Component: any,
179
- props: any,
180
- children: any,
181
- refOrContext: any,
182
-): any {
183
- // This must be called after every function component to prevent hooks from
184
- // being used in classes.
185
-
186
- while (didScheduleRenderPhaseUpdate) {
187
- // Updates were scheduled during the render phase. They are stored in
188
- // the `renderPhaseUpdates` map. Call the component again, reusing the
189
- // work-in-progress hooks and applying the additional updates on top. Keep
190
- // restarting until no more updates are scheduled.
191
- didScheduleRenderPhaseUpdate = false;
192
- numberOfReRenders += 1;
193
-
194
- // Start over from the beginning of the list
195
- workInProgressHook = null;
196
-
197
- children = Component(props, refOrContext);
198
- }
199
- resetHooksState();
200
- return children;
201
-}
202
-
203
-// Reset the internal hooks state if an error occurs while rendering a component
204
-export function resetHooksState(): void {
205
- if (__DEV__) {
206
- isInHookUserCodeInDev = false;
207
- }
208
-
209
- currentlyRenderingComponent = null;
210
- didScheduleRenderPhaseUpdate = false;
211
- firstWorkInProgressHook = null;
212
- numberOfReRenders = 0;
213
- renderPhaseUpdates = null;
214
- workInProgressHook = null;
215
-}
216
-
217
-function getCacheSignal() {
218
- throw new Error('Not implemented.');
219
-}
220
-
221
-function getCacheForType<T>(resourceType: () => T): T {
222
- throw new Error('Not implemented.');
223
-}
224
-
225
-function readContext<T>(context: ReactContext<T>): T {
226
- const threadID = currentPartialRenderer.threadID;
227
- validateContextBounds(context, threadID);
228
- if (__DEV__) {
229
- if (isInHookUserCodeInDev) {
230
- console.error(
231
- 'Context can only be read while React is rendering. ' +
232
- 'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
233
- 'In function components, you can read it directly in the function body, but not ' +
234
- 'inside Hooks like useReducer() or useMemo().',
235
- );
236
- }
237
- }
238
- return context[threadID];
239
-}
240
-
241
-function useContext<T>(context: ReactContext<T>): T {
242
- if (__DEV__) {
243
- currentHookNameInDev = 'useContext';
244
- }
245
- resolveCurrentlyRenderingComponent();
246
- const threadID = currentPartialRenderer.threadID;
247
- validateContextBounds(context, threadID);
248
- return context[threadID];
249
-}
250
-
251
-function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
252
- // $FlowFixMe: Flow doesn't like mixed types
253
- return typeof action === 'function' ? action(state) : action;
254
-}
255
-
256
-export function useState<S>(
257
- initialState: (() => S) | S,
258
-): [S, Dispatch<BasicStateAction<S>>] {
259
- if (__DEV__) {
260
- currentHookNameInDev = 'useState';
261
- }
262
- return useReducer(
263
- basicStateReducer,
264
- // useReducer has a special case to support lazy useState initializers
265
- (initialState: any),
266
- );
267
-}
268
-
269
-export function useReducer<S, I, A>(
270
- reducer: (S, A) => S,
271
- initialArg: I,
272
- init?: I => S,
273
-): [S, Dispatch<A>] {
274
- if (__DEV__) {
275
- if (reducer !== basicStateReducer) {
276
- currentHookNameInDev = 'useReducer';
277
- }
278
- }
279
- currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
280
- workInProgressHook = createWorkInProgressHook();
281
- if (isReRender) {
282
- // This is a re-render. Apply the new render phase updates to the previous
283
- // current hook.
284
- const queue: UpdateQueue<A> = (workInProgressHook.queue: any);
285
- const dispatch: Dispatch<A> = (queue.dispatch: any);
286
- if (renderPhaseUpdates !== null) {
287
- // Render phase updates are stored in a map of queue -> linked list
288
- const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
289
- if (firstRenderPhaseUpdate !== undefined) {
290
- renderPhaseUpdates.delete(queue);
291
- let newState = workInProgressHook.memoizedState;
292
- let update = firstRenderPhaseUpdate;
293
- do {
294
- // Process this render phase update. We don't have to check the
295
- // priority because it will always be the same as the current
296
- // render's.
297
- const action = update.action;
298
- if (__DEV__) {
299
- isInHookUserCodeInDev = true;
300
- }
301
- newState = reducer(newState, action);
302
- if (__DEV__) {
303
- isInHookUserCodeInDev = false;
304
- }
305
- update = update.next;
306
- } while (update !== null);
307
-
308
- workInProgressHook.memoizedState = newState;
309
-
310
- return [newState, dispatch];
311
- }
312
- }
313
- return [workInProgressHook.memoizedState, dispatch];
314
- } else {
315
- if (__DEV__) {
316
- isInHookUserCodeInDev = true;
317
- }
318
- let initialState;
319
- if (reducer === basicStateReducer) {
320
- // Special case for `useState`.
321
- initialState =
322
- typeof initialArg === 'function'
323
- ? ((initialArg: any): () => S)()
324
- : ((initialArg: any): S);
325
- } else {
326
- initialState =
327
- init !== undefined ? init(initialArg) : ((initialArg: any): S);
328
- }
329
- if (__DEV__) {
330
- isInHookUserCodeInDev = false;
331
- }
332
- workInProgressHook.memoizedState = initialState;
333
- const queue: UpdateQueue<A> = (workInProgressHook.queue = {
334
- last: null,
335
- dispatch: null,
336
- });
337
- const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
338
- null,
339
- currentlyRenderingComponent,
340
- queue,
341
- ): any));
342
- return [workInProgressHook.memoizedState, dispatch];
343
- }
344
-}
345
-
346
-function useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T {
347
- currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
348
- workInProgressHook = createWorkInProgressHook();
349
-
350
- const nextDeps = deps === undefined ? null : deps;
351
-
352
- if (workInProgressHook !== null) {
353
- const prevState = workInProgressHook.memoizedState;
354
- if (prevState !== null) {
355
- if (nextDeps !== null) {
356
- const prevDeps = prevState[1];
357
- if (areHookInputsEqual(nextDeps, prevDeps)) {
358
- return prevState[0];
359
- }
360
- }
361
- }
362
- }
363
-
364
- if (__DEV__) {
365
- isInHookUserCodeInDev = true;
366
- }
367
- const nextValue = nextCreate();
368
- if (__DEV__) {
369
- isInHookUserCodeInDev = false;
370
- }
371
- workInProgressHook.memoizedState = [nextValue, nextDeps];
372
- return nextValue;
373
-}
374
-
375
-function useRef<T>(initialValue: T): {|current: T|} {
376
- currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
377
- workInProgressHook = createWorkInProgressHook();
378
- const previousRef = workInProgressHook.memoizedState;
379
- if (previousRef === null) {
380
- const ref = {current: initialValue};
381
- if (__DEV__) {
382
- Object.seal(ref);
383
- }
384
- workInProgressHook.memoizedState = ref;
385
- return ref;
386
- } else {
387
- return previousRef;
388
- }
389
-}
390
-
391
-function useInsertionEffect(
392
- create: () => mixed,
393
- inputs: Array<mixed> | void | null,
394
-) {
395
- if (__DEV__) {
396
- currentHookNameInDev = 'useInsertionEffect';
397
- console.error(
398
- 'useInsertionEffect does nothing on the server, because its effect cannot ' +
399
- "be encoded into the server renderer's output format. This will lead " +
400
- 'to a mismatch between the initial, non-hydrated UI and the intended ' +
401
- 'UI. To avoid this, useInsertionEffect should only be used in ' +
402
- 'components that render exclusively on the client.',
403
- );
404
- }
405
-}
406
-
407
-export function useLayoutEffect(
408
- create: () => (() => void) | void,
409
- inputs: Array<mixed> | void | null,
410
-) {
411
- if (__DEV__) {
412
- currentHookNameInDev = 'useLayoutEffect';
413
- console.error(
414
- 'useLayoutEffect does nothing on the server, because its effect cannot ' +
415
- "be encoded into the server renderer's output format. This will lead " +
416
- 'to a mismatch between the initial, non-hydrated UI and the intended ' +
417
- 'UI. To avoid this, useLayoutEffect should only be used in ' +
418
- 'components that render exclusively on the client. ' +
419
- 'See https://reactjs.org/link/uselayouteffect-ssr for common fixes.',
420
- );
421
- }
422
-}
423
-
424
-function dispatchAction<A>(
425
- componentIdentity: Object,
426
- queue: UpdateQueue<A>,
427
- action: A,
428
-) {
429
- if (numberOfReRenders >= RE_RENDER_LIMIT) {
430
- throw new Error(
431
- 'Too many re-renders. React limits the number of renders to prevent ' +
432
- 'an infinite loop.',
433
- );
434
- }
435
-
436
- if (componentIdentity === currentlyRenderingComponent) {
437
- // This is a render phase update. Stash it in a lazily-created map of
438
- // queue -> linked list of updates. After this render pass, we'll restart
439
- // and apply the stashed updates on top of the work-in-progress hook.
440
- didScheduleRenderPhaseUpdate = true;
441
- const update: Update<A> = {
442
- action,
443
- next: null,
444
- };
445
- if (renderPhaseUpdates === null) {
446
- renderPhaseUpdates = new Map();
447
- }
448
- const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
449
- if (firstRenderPhaseUpdate === undefined) {
450
- renderPhaseUpdates.set(queue, update);
451
- } else {
452
- // Append the update to the end of the list.
453
- let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
454
- while (lastRenderPhaseUpdate.next !== null) {
455
- lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
456
- }
457
- lastRenderPhaseUpdate.next = update;
458
- }
459
- } else {
460
- // This means an update has happened after the function component has
461
- // returned. On the server this is a no-op. In React Fiber, the update
462
- // would be scheduled for a future render.
463
- }
464
-}
465
-
466
-export function useCallback<T>(
467
- callback: T,
468
- deps: Array<mixed> | void | null,
469
-): T {
470
- return useMemo(() => callback, deps);
471
-}
472
-
473
-// TODO Decide on how to implement this hook for server rendering.
474
-// If a mutation occurs during render, consider triggering a Suspense boundary
475
-// and falling back to client rendering.
476
-function useMutableSource<Source, Snapshot>(
477
- source: MutableSource<Source>,
478
- getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
479
- subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
480
-): Snapshot {
481
- resolveCurrentlyRenderingComponent();
482
- return getSnapshot(source._source);
483
-}
484
-
485
-function useSyncExternalStore<T>(
486
- subscribe: (() => void) => () => void,
487
- getSnapshot: () => T,
488
- getServerSnapshot?: () => T,
489
-): T {
490
- if (getServerSnapshot === undefined) {
491
- throw new Error(
492
- 'Missing getServerSnapshot, which is required for ' +
493
- 'server-rendered content. Will revert to client rendering.',
494
- );
495
- }
496
- return getServerSnapshot();
497
-}
498
-
499
-function useDeferredValue<T>(value: T): T {
500
- resolveCurrentlyRenderingComponent();
501
- return value;
502
-}
503
-
504
-function useTransition(): [boolean, (callback: () => void) => void] {
505
- resolveCurrentlyRenderingComponent();
506
- const startTransition = callback => {
507
- callback();
508
- };
509
- return [false, startTransition];
510
-}
511
-
512
-function useId(): string {
513
- throw new Error('Not implemented.');
514
-}
515
-
516
-function useCacheRefresh(): <T>(?() => T, ?T) => void {
517
- throw new Error('Not implemented.');
518
-}
519
-
520
-function noop(): void {}
521
-
522
-export let currentPartialRenderer: PartialRenderer = (null: any);
523
-export function setCurrentPartialRenderer(renderer: PartialRenderer) {
524
- currentPartialRenderer = renderer;
525
-}
526
-
527
-export const Dispatcher: DispatcherType = {
528
- readContext,
529
- useContext,
530
- useMemo,
531
- useReducer,
532
- useRef,
533
- useState,
534
- useInsertionEffect,
535
- useLayoutEffect,
536
- useCallback,
537
- // useImperativeHandle is not run in the server environment
538
- useImperativeHandle: noop,
539
- // Effects are not run in the server environment.
540
- useEffect: noop,
541
- // Debugging effect
542
- useDebugValue: noop,
543
- useDeferredValue,
544
- useTransition,
545
- useId,
546
- // Subscriptions are not setup in a server environment.
547
- useMutableSource,
548
- useSyncExternalStore,
549
-};
550
-
551
-if (enableCache) {
552
- Dispatcher.getCacheSignal = getCacheSignal;
553
- Dispatcher.getCacheForType = getCacheForType;
554
- Dispatcher.useCacheRefresh = useCacheRefresh;
555
-}