Reuse request so that a ReabableStream body does not become disturbed (#26771)
Andrew Gadzik committed
May 3, 2023 at 15:06 UTC
2c1117a8d008e34eb77eaaf7b8ef315a38644735
2 files changed
+23
-1
packages/react/src/ReactFetch.js
+7
-1
@@ -74,7 +74,13 @@ if (enableCache && enableFetchInstrumentation) {
74
url = resource;
75
} else {
76
// Normalize the request.
77
- const request = new Request(resource, options);
77
+ // if resource is not a string or a URL (its an instance of Request)
78
+ // then do not instantiate a new Request but instead
79
+ // reuse the request as to not disturb the body in the event it's a ReadableStream.
80
+ const request =
81
+ typeof resource === 'string' || resource instanceof URL
82
+ ? new Request(resource, options)
83
+ : resource;
84
if (
85
(request.method !== 'GET' && request.method !== 'HEAD') ||
86
// $FlowFixMe[prop-missing]: keepalive is real
packages/react/src/__tests__/ReactFetch-test.js
+16
@@ -135,6 +135,22 @@ describe('ReactFetch', () => {
135
expect(fetchCount).toBe(1);
136
});
137
138
+ // @gate enableFetchInstrumentation && enableCache
139
+ it('can dedupe fetches using URL and not', async () => {
140
+ const url = 'http://example.com/';
141
+ function Component() {
142
+ const response = use(fetch(url));
143
+ const text = use(response.text());
144
+ const response2 = use(fetch(new URL(url)));
145
+ const text2 = use(response2.text());
146
+ return text + ' ' + text2;
147
+ }
148
+ expect(await render(Component)).toMatchInlineSnapshot(
149
+ `"GET ${url} [] GET ${url} []"`,
150
+ );
151
+ expect(fetchCount).toBe(1);
152
+ });
153
+
154
it('can opt-out of deduping fetches inside of render with custom signal', async () => {
155
const controller = new AbortController();
156
function useCustomHook() {