Skip to content

Commit 149d577

Browse files
committed
chains host dispatchers. The idea here is that host dispatchers are not bound to renders so we need to be able to dispatch to them at any time. This updates the implementation to chain these dispatchers so that each renderer can respond to the dispatch. Semantically we don't always want every renderer to do this for instance if Fizz handles a float method we don't want Fiber to as well so each dispatcher implementation can decide if it makes sense to forward the call or not. For float methods server disaptchers will handle the call if they can resolve a Request otherwise they will forward. For client dispatchers they will handle the call and always forward. The choice needs to be made for each dispatcher method and may have implications on correct renderer import order. For now we just live with the restriction that if you want to use server and client together (such as renderToString in the browser) you need to import the server renderer after the client renderer.
1 parent 1c02b9d commit 149d577

20 files changed

+187
-172
lines changed

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
* @flow
88
*/
99

10-
import type {HostDispatcher} from 'react-dom/src/shared/ReactDOMTypes';
1110
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
1211
import type {DOMEventName} from '../events/DOMEventNames';
1312
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
@@ -107,6 +106,10 @@ import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
107106
import {validateLinkPropsForStyleResource} from '../shared/ReactDOMResourceValidation';
108107
import escapeSelectorAttributeValueInsideDoubleQuotes from './escapeSelectorAttributeValueInsideDoubleQuotes';
109108

109+
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
110+
const ReactDOMCurrentDispatcher =
111+
ReactDOMSharedInternals.ReactDOMCurrentDispatcher;
112+
110113
export type Type = string;
111114
export type Props = {
112115
autoFocus?: boolean,
@@ -2108,10 +2111,8 @@ function getDocumentFromRoot(root: HoistableRoot): Document {
21082111
return root.ownerDocument || root;
21092112
}
21102113

2111-
// We want this to be the default dispatcher on ReactDOMSharedInternals but we don't want to mutate
2112-
// internals in Module scope. Instead we export it and Internals will import it. There is already a cycle
2113-
// from Internals -> ReactDOM -> HostConfig -> Internals so this doesn't introduce a new one.
2114-
export const ReactDOMClientDispatcher: HostDispatcher = {
2114+
const previousDispatcher = ReactDOMCurrentDispatcher.current;
2115+
ReactDOMCurrentDispatcher.current = {
21152116
prefetchDNS,
21162117
preconnect,
21172118
preload,
@@ -2162,20 +2163,23 @@ function prefetchDNS(href: string) {
21622163
if (!enableFloat) {
21632164
return;
21642165
}
2166+
previousDispatcher.prefetchDNS(href);
21652167
preconnectAs('dns-prefetch', href, null);
21662168
}
21672169
21682170
function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
21692171
if (!enableFloat) {
21702172
return;
21712173
}
2174+
previousDispatcher.preconnect(href, crossOrigin);
21722175
preconnectAs('preconnect', href, crossOrigin);
21732176
}
21742177
21752178
function preload(href: string, as: string, options?: ?PreloadImplOptions) {
21762179
if (!enableFloat) {
21772180
return;
21782181
}
2182+
previousDispatcher.preload(href, as, options);
21792183
const ownerDocument = getDocumentForImperativeFloatMethods();
21802184
if (href && as && ownerDocument) {
21812185
let preloadSelector = `link[rel="preload"][as="${escapeSelectorAttributeValueInsideDoubleQuotes(
@@ -2256,6 +2260,7 @@ function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
22562260
if (!enableFloat) {
22572261
return;
22582262
}
2263+
previousDispatcher.preloadModule(href, options);
22592264
const ownerDocument = getDocumentForImperativeFloatMethods();
22602265
if (href) {
22612266
const as =
@@ -2319,6 +2324,7 @@ function preinitStyle(
23192324
if (!enableFloat) {
23202325
return;
23212326
}
2327+
previousDispatcher.preinitStyle(href, precedence, options);
23222328
const ownerDocument = getDocumentForImperativeFloatMethods();
23232329

23242330
if (href) {
@@ -2395,6 +2401,7 @@ function preinitScript(src: string, options?: ?PreinitScriptOptions) {
23952401
if (!enableFloat) {
23962402
return;
23972403
}
2404+
previousDispatcher.preinitScript(src, options);
23982405
const ownerDocument = getDocumentForImperativeFloatMethods();
23992406

24002407
if (src) {
@@ -2453,6 +2460,7 @@ function preinitModuleScript(
24532460
if (!enableFloat) {
24542461
return;
24552462
}
2463+
previousDispatcher.preinitModuleScript(src, options);
24562464
const ownerDocument = getDocumentForImperativeFloatMethods();
24572465

24582466
if (src) {

packages/react-dom-bindings/src/server/ReactDOMFlightServerHostDispatcher.js

+30-12
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
*/
99

1010
import type {
11-
HostDispatcher,
1211
CrossOriginEnum,
1312
PreloadImplOptions,
1413
PreloadModuleImplOptions,
@@ -25,7 +24,12 @@ import {
2524
resolveRequest,
2625
} from 'react-server/src/ReactFlightServer';
2726

28-
export const ReactDOMFlightServerDispatcher: HostDispatcher = {
27+
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
28+
const ReactDOMCurrentDispatcher =
29+
ReactDOMSharedInternals.ReactDOMCurrentDispatcher;
30+
31+
const previousDispatcher = ReactDOMCurrentDispatcher.current;
32+
ReactDOMCurrentDispatcher.current = {
2933
prefetchDNS,
3034
preconnect,
3135
preload,
@@ -48,6 +52,8 @@ function prefetchDNS(href: string) {
4852
}
4953
hints.add(key);
5054
emitHint(request, 'D', href);
55+
} else {
56+
previousDispatcher.prefetchDNS(href);
5157
}
5258
}
5359
}
@@ -71,6 +77,8 @@ function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
7177
} else {
7278
emitHint(request, 'C', href);
7379
}
80+
} else {
81+
previousDispatcher.preconnect(href, crossOrigin);
7482
}
7583
}
7684
}
@@ -104,6 +112,8 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) {
104112
} else {
105113
emitHint(request, 'L', [href, as]);
106114
}
115+
} else {
116+
previousDispatcher.preload(href, as, options);
107117
}
108118
}
109119
}
@@ -128,6 +138,8 @@ function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
128138
} else {
129139
return emitHint(request, 'm', href);
130140
}
141+
} else {
142+
previousDispatcher.preloadModule(href, options);
131143
}
132144
}
133145
}
@@ -162,18 +174,20 @@ function preinitStyle(
162174
} else {
163175
return emitHint(request, 'S', href);
164176
}
177+
} else {
178+
previousDispatcher.preinitStyle(href, precedence, options);
165179
}
166180
}
167181
}
168182
}
169183

170-
function preinitScript(href: string, options?: ?PreinitScriptOptions) {
184+
function preinitScript(src: string, options?: ?PreinitScriptOptions) {
171185
if (enableFloat) {
172-
if (typeof href === 'string') {
186+
if (typeof src === 'string') {
173187
const request = resolveRequest();
174188
if (request) {
175189
const hints = getHints(request);
176-
const key = 'X|' + href;
190+
const key = 'X|' + src;
177191
if (hints.has(key)) {
178192
// duplicate hint
179193
return;
@@ -182,25 +196,27 @@ function preinitScript(href: string, options?: ?PreinitScriptOptions) {
182196

183197
const trimmed = trimOptions(options);
184198
if (trimmed) {
185-
return emitHint(request, 'X', [href, trimmed]);
199+
return emitHint(request, 'X', [src, trimmed]);
186200
} else {
187-
return emitHint(request, 'X', href);
201+
return emitHint(request, 'X', src);
188202
}
203+
} else {
204+
previousDispatcher.preinitScript(src, options);
189205
}
190206
}
191207
}
192208
}
193209

194210
function preinitModuleScript(
195-
href: string,
211+
src: string,
196212
options?: ?PreinitModuleScriptOptions,
197213
) {
198214
if (enableFloat) {
199-
if (typeof href === 'string') {
215+
if (typeof src === 'string') {
200216
const request = resolveRequest();
201217
if (request) {
202218
const hints = getHints(request);
203-
const key = 'M|' + href;
219+
const key = 'M|' + src;
204220
if (hints.has(key)) {
205221
// duplicate hint
206222
return;
@@ -209,10 +225,12 @@ function preinitModuleScript(
209225

210226
const trimmed = trimOptions(options);
211227
if (trimmed) {
212-
return emitHint(request, 'M', [href, trimmed]);
228+
return emitHint(request, 'M', [src, trimmed]);
213229
} else {
214-
return emitHint(request, 'M', href);
230+
return emitHint(request, 'M', src);
215231
}
232+
} else {
233+
previousDispatcher.preinitModuleScript(src, options);
216234
}
217235
}
218236
}

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,20 @@ import {getValueDescriptorExpectingObjectForWarning} from '../shared/ReactDOMRes
8888
import {NotPending} from '../shared/ReactDOMFormActions';
8989

9090
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
91-
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
91+
const ReactDOMCurrentDispatcher =
92+
ReactDOMSharedInternals.ReactDOMCurrentDispatcher;
9293

93-
const ReactDOMServerDispatcher = {
94+
const previousDispatcher = ReactDOMCurrentDispatcher.current;
95+
ReactDOMCurrentDispatcher.current = {
9496
prefetchDNS,
9597
preconnect,
9698
preload,
9799
preloadModule,
98-
preinitStyle,
99100
preinitScript,
101+
preinitStyle,
100102
preinitModuleScript,
101103
};
102104

103-
export function prepareHostDispatcher() {
104-
ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher;
105-
}
106-
107105
// We make every property of the descriptor optional because it is not a contract that
108106
// the headers provided by onHeaders has any particular header types.
109107
export type HeadersDescriptor = {
@@ -5342,6 +5340,7 @@ function prefetchDNS(href: string) {
53425340
// the resources for this call in either case we opt to do nothing. We can consider making this a warning
53435341
// but there may be times where calling a function outside of render is intentional (i.e. to warm up data
53445342
// fetching) and we don't want to warn in those cases.
5343+
previousDispatcher.prefetchDNS(href);
53455344
return;
53465345
}
53475346
const resumableState = getResumableState(request);
@@ -5397,6 +5396,7 @@ function preconnect(href: string, crossOrigin: ?CrossOriginEnum) {
53975396
// the resources for this call in either case we opt to do nothing. We can consider making this a warning
53985397
// but there may be times where calling a function outside of render is intentional (i.e. to warm up data
53995398
// fetching) and we don't want to warn in those cases.
5399+
previousDispatcher.preconnect(href, crossOrigin);
54005400
return;
54015401
}
54025402
const resumableState = getResumableState(request);
@@ -5460,6 +5460,7 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) {
54605460
// the resources for this call in either case we opt to do nothing. We can consider making this a warning
54615461
// but there may be times where calling a function outside of render is intentional (i.e. to warm up data
54625462
// fetching) and we don't want to warn in those cases.
5463+
previousDispatcher.preload(href, as, options);
54635464
return;
54645465
}
54655466
const resumableState = getResumableState(request);
@@ -5663,6 +5664,7 @@ function preloadModule(
56635664
// the resources for this call in either case we opt to do nothing. We can consider making this a warning
56645665
// but there may be times where calling a function outside of render is intentional (i.e. to warm up data
56655666
// fetching) and we don't want to warn in those cases.
5667+
previousDispatcher.preloadModule(href, options);
56665668
return;
56675669
}
56685670
const resumableState = getResumableState(request);
@@ -5739,6 +5741,7 @@ function preinitStyle(
57395741
// the resources for this call in either case we opt to do nothing. We can consider making this a warning
57405742
// but there may be times where calling a function outside of render is intentional (i.e. to warm up data
57415743
// fetching) and we don't want to warn in those cases.
5744+
previousDispatcher.preinitStyle(href, precedence, options);
57425745
return;
57435746
}
57445747
const resumableState = getResumableState(request);
@@ -5826,6 +5829,7 @@ function preinitScript(src: string, options?: ?PreinitScriptOptions): void {
58265829
// the resources for this call in either case we opt to do nothing. We can consider making this a warning
58275830
// but there may be times where calling a function outside of render is intentional (i.e. to warm up data
58285831
// fetching) and we don't want to warn in those cases.
5832+
previousDispatcher.preinitScript(src, options);
58295833
return;
58305834
}
58315835
const resumableState = getResumableState(request);
@@ -5891,6 +5895,7 @@ function preinitModuleScript(
58915895
// the resources for this call in either case we opt to do nothing. We can consider making this a warning
58925896
// but there may be times where calling a function outside of render is intentional (i.e. to warm up data
58935897
// fetching) and we don't want to warn in those cases.
5898+
previousDispatcher.preinitModuleScript(src, options);
58945899
return;
58955900
}
58965901
const resumableState = getResumableState(request);

packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js

-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ export {
163163
writeHoistables,
164164
writePostamble,
165165
hoistHoistables,
166-
prepareHostDispatcher,
167166
resetResumableState,
168167
completeResumableState,
169168
emitEarlyPreloads,

packages/react-dom-bindings/src/server/ReactFlightServerConfigDOM.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,9 @@ import type {
1616
PreinitModuleScriptOptions,
1717
} from 'react-dom/src/shared/ReactDOMTypes';
1818

19-
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
20-
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
21-
22-
import {ReactDOMFlightServerDispatcher} from './ReactDOMFlightServerHostDispatcher';
23-
24-
export function prepareHostDispatcher(): void {
25-
ReactDOMCurrentDispatcher.current = ReactDOMFlightServerDispatcher;
26-
}
19+
// This module registers the host dispatcher so it needs to be imported
20+
// but it does not have any exports
21+
import './ReactDOMFlightServerHostDispatcher';
2722

2823
// Used to distinguish these contexts from ones used in other renderers.
2924
// E.g. this can be used to distinguish legacy renderers from this modern one.

0 commit comments

Comments
 (0)