Files
react/packages/react-fetch/src/ReactFetchBrowser.js
Richard Maisano c512aa0081 [Blocks] Scaffolding react-fetch + first pass at node implementation (#18863)
* First pass at scaffolding out the Node implementation of react-data.

While incomplete, this patch contains some changes to the react-data
package in order to start adding support for Node.

The first part of this change accounts for splitting react-data/fetch
into two discrete entries, adding (and defaulting to) the Node
implementation.

The second part is sketching out a rough approximation of `fetch` for
Node. This implementation is not complete by any means, but provides a
starting point.

* Remove NodeFetch module and put it directly into ReactDataFetchNode.

* Replaced react-data with react-fetch.

This patch shuffles around some of the scaffolding that was in
react-data in favor of react-fetch. It also removes the additional
"fetch" package in favor of something flatter.

* Tweak package organization

* Simplify and add a test

Co-authored-by: Dan Abramov <dan.abramov@me.com>
2020-05-12 17:21:45 +01:00

154 lines
3.5 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {Wakeable} from 'shared/ReactTypes';
import {readCache} from 'react/unstable-cache';
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
type PendingResult = {|
status: 0,
value: Wakeable,
|};
type ResolvedResult = {|
status: 1,
value: mixed,
|};
type RejectedResult = {|
status: 2,
value: mixed,
|};
type Result = PendingResult | ResolvedResult | RejectedResult;
// TODO: this is a browser-only version. Add a separate Node entry point.
const nativeFetch = window.fetch;
const fetchKey = {};
function readResultMap(): Map<string, Result> {
const resources = readCache().resources;
let map = resources.get(fetchKey);
if (map === undefined) {
map = new Map();
resources.set(fetchKey, map);
}
return map;
}
function toResult(thenable): Result {
const result: Result = {
status: Pending,
value: thenable,
};
thenable.then(
value => {
if (result.status === Pending) {
const resolvedResult = ((result: any): ResolvedResult);
resolvedResult.status = Resolved;
resolvedResult.value = value;
}
},
err => {
if (result.status === Pending) {
const rejectedResult = ((result: any): RejectedResult);
rejectedResult.status = Rejected;
rejectedResult.value = err;
}
},
);
return result;
}
function readResult(result: Result) {
if (result.status === Resolved) {
return result.value;
} else {
throw result.value;
}
}
function Response(nativeResponse) {
this.headers = nativeResponse.headers;
this.ok = nativeResponse.ok;
this.redirected = nativeResponse.redirected;
this.status = nativeResponse.status;
this.statusText = nativeResponse.statusText;
this.type = nativeResponse.type;
this.url = nativeResponse.url;
this._response = nativeResponse;
this._arrayBuffer = null;
this._blob = null;
this._json = null;
this._text = null;
}
Response.prototype = {
constructor: Response,
arrayBuffer() {
return readResult(
this._arrayBuffer ||
(this._arrayBuffer = toResult(this._response.arrayBuffer())),
);
},
blob() {
return readResult(
this._blob || (this._blob = toResult(this._response.blob())),
);
},
json() {
return readResult(
this._json || (this._json = toResult(this._response.json())),
);
},
text() {
return readResult(
this._text || (this._text = toResult(this._response.text())),
);
},
};
function preloadResult(url: string, options: mixed): Result {
const map = readResultMap();
let entry = map.get(url);
if (!entry) {
if (options) {
if (options.method || options.body || options.signal) {
// TODO: wire up our own cancellation mechanism.
// TODO: figure out what to do with POST.
throw Error('Unsupported option');
}
}
const thenable = nativeFetch(url, options);
entry = toResult(thenable);
map.set(url, entry);
}
return entry;
}
export function preload(url: string, options: mixed): void {
preloadResult(url, options);
// Don't return anything.
}
export function fetch(url: string, options: mixed): Object {
const result = preloadResult(url, options);
const nativeResponse = (readResult(result): any);
if (nativeResponse._reactResponse) {
return nativeResponse._reactResponse;
} else {
return (nativeResponse._reactResponse = new Response(nativeResponse));
}
}