[Float] support fetchpriority on `ReactDOM.preload()` and `ReactDOM.preinit()` (#26880)
exposes fetchPriority as an option for `ReactDOM.preload()` and `ReactDOM.preinit()` the typings should be `'high' | 'low' | 'auto'`
Josh Story committed
Jun 1, 2023 at 13:10 UTC
042d8f606ce643d2eca955badbf07ea5b8ac266c
4 files changed
+211
-2
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+5
@@ -2173,6 +2173,7 @@ type PreloadOptions = {
2173
crossOrigin?: string,
2174
integrity?: string,
2175
type?: string,
2176
+ fetchPriority?: 'high' | 'low' | 'auto',
2177
};
2178
function preload(href: string, options: PreloadOptions) {
2179
if (!enableFloat) {
@@ -2245,6 +2246,7 @@ function preloadPropsFromPreloadOptions(
2246
crossOrigin: as === 'font' ? '' : options.crossOrigin,
2247
integrity: options.integrity,
2248
type: options.type,
2249
+ fetchPriority: options.fetchPriority,
2250
};
2251
}
2252
@@ -2254,6 +2256,7 @@ type PreinitOptions = {
2256
crossOrigin?: string,
2257
integrity?: string,
2258
nonce?: string,
2259
+ fetchPriority?: 'high' | 'low' | 'auto',
2260
};
2261
function preinit(href: string, options: PreinitOptions) {
2262
if (!enableFloat) {
@@ -2395,6 +2398,7 @@ function stylesheetPropsFromPreinitOptions(
2398
'data-precedence': precedence,
2399
crossOrigin: options.crossOrigin,
2400
integrity: options.integrity,
2401
+ fetchPriority: options.fetchPriority,
2402
};
2403
}
2404
@@ -2408,6 +2412,7 @@ function scriptPropsFromPreinitOptions(
2412
crossOrigin: options.crossOrigin,
2413
integrity: options.integrity,
2414
nonce: options.nonce,
2415
+ fetchPriority: options.fetchPriority,
2416
};
2417
}
2418
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+5
@@ -5102,6 +5102,7 @@ type PreloadOptions = {
5102
crossOrigin?: string,
5103
integrity?: string,
5104
type?: string,
5105
+ fetchPriority?: 'high' | 'low' | 'auto',
5106
};
5107
export function preload(href: string, options: PreloadOptions) {
5108
if (!enableFloat) {
@@ -5247,6 +5248,7 @@ type PreinitOptions = {
5248
crossOrigin?: string,
5249
integrity?: string,
5250
nonce?: string,
5251
+ fetchPriority?: 'high' | 'low' | 'auto',
5252
};
5253
function preinit(href: string, options: PreinitOptions): void {
5254
if (!enableFloat) {
@@ -5590,6 +5592,7 @@ function preloadPropsFromPreloadOptions(
5592
crossOrigin: as === 'font' ? '' : options.crossOrigin,
5593
integrity: options.integrity,
5594
type: options.type,
5595
+ fetchPriority: options.fetchPriority,
5596
};
5597
}
5598
@@ -5631,6 +5634,7 @@ function stylesheetPropsFromPreinitOptions(
5634
'data-precedence': precedence,
5635
crossOrigin: options.crossOrigin,
5636
integrity: options.integrity,
5637
+ fetchPriority: options.fetchPriority,
5638
};
5639
}
5640
@@ -5662,6 +5666,7 @@ function scriptPropsFromPreinitOptions(
5666
crossOrigin: options.crossOrigin,
5667
integrity: options.integrity,
5668
nonce: options.nonce,
5669
+ fetchPriority: options.fetchPriority,
5670
};
5671
}
5672
packages/react-dom/src/ReactDOMDispatcher.js
+2
@@ -14,6 +14,7 @@ export type PreloadOptions = {
14
crossOrigin?: string,
15
integrity?: string,
16
type?: string,
17
+ fetchPriority?: 'high' | 'low' | 'auto',
18
};
19
export type PreinitOptions = {
20
as: string,
@@ -21,6 +22,7 @@ export type PreinitOptions = {
22
crossOrigin?: string,
23
integrity?: string,
24
nonce?: string,
25
+ fetchPriority?: 'high' | 'low' | 'auto',
26
};
27
28
export type HostDispatcher = {
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+199
-2
@@ -3789,6 +3789,7 @@ body {
3789
as: 'style',
3790
crossOrigin: 'use-credentials',
3791
integrity: 'some hash',
3792
+ fetchPriority: 'low',
3793
});
3794
return (
3795
<html>
@@ -3909,6 +3910,113 @@ body {
3910
'ReactDOM.preload(): For `href` "foo", The options provided conflict with props on a matching <link rel="stylesheet" ... /> element. When the preload options disagree with the underlying resource it usually means the browser will not be able to use the preload when the resource is fetched, negating any benefit the preload would provide. React will preload the resource using props derived from the resource instead and ignore the options provided to the `ReactDOM.preload()` call. In general, preloading is useful when you expect to render a resource soon but have not yet done so. In this case since the underlying resource was already rendered the preload call may be extraneous. Try removing the call, otherwise try adjusting both the props on the <link rel="stylesheet" ... /> and the options passed to `ReactDOM.preload()` to agree.\n "integrity" missing from options, underlying prop value: "some hash"\n "media" missing from options, underlying prop value: "print"\n "crossOrigin" option value: "use-credentials", missing from underlying props',
3911
]);
3912
});
3913
+
3914
+ it('supports fetchPriority', async () => {
3915
+ function Component({isServer}) {
3916
+ ReactDOM.preload(isServer ? 'highserver' : 'highclient', {
3917
+ as: 'script',
3918
+ fetchPriority: 'high',
3919
+ });
3920
+ ReactDOM.preload(isServer ? 'lowserver' : 'lowclient', {
3921
+ as: 'style',
3922
+ fetchPriority: 'low',
3923
+ });
3924
+ ReactDOM.preload(isServer ? 'autoserver' : 'autoclient', {
3925
+ as: 'style',
3926
+ fetchPriority: 'auto',
3927
+ });
3928
+ return 'hello';
3929
+ }
3930
+
3931
+ await act(() => {
3932
+ renderToPipeableStream(
3933
+ <html>
3934
+ <body>
3935
+ <Component isServer={true} />
3936
+ </body>
3937
+ </html>,
3938
+ ).pipe(writable);
3939
+ });
3940
+
3941
+ expect(getMeaningfulChildren(document)).toEqual(
3942
+ <html>
3943
+ <head>
3944
+ <link
3945
+ rel="preload"
3946
+ as="style"
3947
+ href="lowserver"
3948
+ fetchpriority="low"
3949
+ />
3950
+ <link
3951
+ rel="preload"
3952
+ as="style"
3953
+ href="autoserver"
3954
+ fetchpriority="auto"
3955
+ />
3956
+ <link
3957
+ rel="preload"
3958
+ as="script"
3959
+ href="highserver"
3960
+ fetchpriority="high"
3961
+ />
3962
+ </head>
3963
+ <body>hello</body>
3964
+ </html>,
3965
+ );
3966
+
3967
+ ReactDOMClient.hydrateRoot(
3968
+ document,
3969
+ <html>
3970
+ <body>
3971
+ <Component />
3972
+ </body>
3973
+ </html>,
3974
+ );
3975
+ await waitForAll([]);
3976
+ expect(getMeaningfulChildren(document)).toEqual(
3977
+ <html>
3978
+ <head>
3979
+ <link
3980
+ rel="preload"
3981
+ as="style"
3982
+ href="lowserver"
3983
+ fetchpriority="low"
3984
+ />
3985
+ <link
3986
+ rel="preload"
3987
+ as="style"
3988
+ href="autoserver"
3989
+ fetchpriority="auto"
3990
+ />
3991
+ <link
3992
+ rel="preload"
3993
+ as="script"
3994
+ href="highserver"
3995
+ fetchpriority="high"
3996
+ />
3997
+ <link
3998
+ rel="preload"
3999
+ as="script"
4000
+ href="highclient"
4001
+ fetchpriority="high"
4002
+ />
4003
+ <link
4004
+ rel="preload"
4005
+ as="style"
4006
+ href="lowclient"
4007
+ fetchpriority="low"
4008
+ />
4009
+ <link
4010
+ rel="preload"
4011
+ as="style"
4012
+ href="autoclient"
4013
+ fetchpriority="auto"
4014
+ />
4015
+ </head>
4016
+ <body>hello</body>
4017
+ </html>,
4018
+ );
4019
+ });
4020
});
4021
4022
describe('ReactDOM.preinit(href, { as: ... })', () => {
@@ -4442,7 +4550,6 @@ body {
4550
<body>hello</body>
4551
</html>,
4552
);
4445
-
4553
await clientAct(() => {
4554
ReactDOMClient.hydrateRoot(
4555
document,
@@ -4453,7 +4560,6 @@ body {
4560
</html>,
4561
);
4562
});
4456
-
4563
expect(getMeaningfulChildren(document)).toEqual(
4564
<html>
4565
<head>
@@ -4474,6 +4580,97 @@ body {
4580
</html>,
4581
);
4582
});
4583
+
4584
+ it('supports fetchPriority', async () => {
4585
+ function Component({isServer}) {
4586
+ ReactDOM.preinit(isServer ? 'highserver' : 'highclient', {
4587
+ as: 'script',
4588
+ fetchPriority: 'high',
4589
+ });
4590
+ ReactDOM.preinit(isServer ? 'lowserver' : 'lowclient', {
4591
+ as: 'style',
4592
+ fetchPriority: 'low',
4593
+ });
4594
+ ReactDOM.preinit(isServer ? 'autoserver' : 'autoclient', {
4595
+ as: 'style',
4596
+ fetchPriority: 'auto',
4597
+ });
4598
+ return 'hello';
4599
+ }
4600
+
4601
+ await act(() => {
4602
+ renderToPipeableStream(
4603
+ <html>
4604
+ <body>
4605
+ <Component isServer={true} />
4606
+ </body>
4607
+ </html>,
4608
+ ).pipe(writable);
4609
+ });
4610
+
4611
+ expect(getMeaningfulChildren(document)).toEqual(
4612
+ <html>
4613
+ <head>
4614
+ <link
4615
+ rel="stylesheet"
4616
+ href="lowserver"
4617
+ fetchpriority="low"
4618
+ data-precedence="default"
4619
+ />
4620
+ <link
4621
+ rel="stylesheet"
4622
+ href="autoserver"
4623
+ fetchpriority="auto"
4624
+ data-precedence="default"
4625
+ />
4626
+ <script async="" src="highserver" fetchpriority="high" />
4627
+ </head>
4628
+ <body>hello</body>
4629
+ </html>,
4630
+ );
4631
+ ReactDOMClient.hydrateRoot(
4632
+ document,
4633
+ <html>
4634
+ <body>
4635
+ <Component />
4636
+ </body>
4637
+ </html>,
4638
+ );
4639
+ await waitForAll([]);
4640
+ expect(getMeaningfulChildren(document)).toEqual(
4641
+ <html>
4642
+ <head>
4643
+ <link
4644
+ rel="stylesheet"
4645
+ href="lowserver"
4646
+ fetchpriority="low"
4647
+ data-precedence="default"
4648
+ />
4649
+ <link
4650
+ rel="stylesheet"
4651
+ href="autoserver"
4652
+ fetchpriority="auto"
4653
+ data-precedence="default"
4654
+ />
4655
+ <link
4656
+ rel="stylesheet"
4657
+ href="lowclient"
4658
+ fetchpriority="low"
4659
+ data-precedence="default"
4660
+ />
4661
+ <link
4662
+ rel="stylesheet"
4663
+ href="autoclient"
4664
+ fetchpriority="auto"
4665
+ data-precedence="default"
4666
+ />
4667
+ <script async="" src="highserver" fetchpriority="high" />
4668
+ <script async="" src="highclient" fetchpriority="high" />
4669
+ </head>
4670
+ <body>hello</body>
4671
+ </html>,
4672
+ );
4673
+ });
4674
});
4675
4676
describe('Stylesheet Resources', () => {