Skip to content
This repository was archived by the owner on Aug 3, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 178 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This library abstracts those features into a generic HTTP component.
✓ Uses the native [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API
✓ Smart deduping of requests
✓ Powerful and customizable response caching
Compose requests
Support for parallel requests
✓ Polling (coming soon)
✓ Small footprint (~2kb gzipped)

Expand All @@ -37,15 +37,15 @@ yarn add react-request

### Getting Started

Here's a simple example of using React Request.
Here's a quick look at what using React Request is like:

```js
import { Request } from 'react-request';
import { Fetch } from 'react-request';

class App extends Component {
render() {
return (
<Request
<Fetch
url="https://jsonplaceholder.typicode.com/posts/1"
render={({ fetching, error, data }) => {
if (fetching) {
Expand All @@ -69,24 +69,19 @@ class App extends Component {
}
```

> Note: the name given to this library in the above example will shadow the
> [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request)
> constructor. Most people do not use the Request constructor directly, but if
> you prefer it you can use another name, such as `Req`, instead.

Need to make multiple requests? We got you.

```js
import { RequestComposer } from 'react-request';
import { FetchComposer } from 'react-request';

class App extends Component {
render() {
return (
<RequestComposer
<FetchComposer
requests={[
<Request url="https://jsonplaceholder.typicode.com/posts/1" />,
<Request url="https://jsonplaceholder.typicode.com/posts/2" />,
<Request url="https://jsonplaceholder.typicode.com/posts/3" />
<Fetch url="https://jsonplaceholder.typicode.com/posts/1" />,
<Fetch url="https://jsonplaceholder.typicode.com/posts/2" />,
<Fetch url="https://jsonplaceholder.typicode.com/posts/3" />
]}
render={([postOne, postTwo, postThree]) => {
return (
Expand Down Expand Up @@ -117,7 +112,175 @@ Check out the API reference below for more.

### API

Documentation coming soon.
This library has two exports:

* `Fetch`: A component for making a single HTTP request
* `FetchComposer`: A component for making parallel HTTP requests

#### `<Fetch />`

A component for making a single HTTP request. It accepts every value of `init` and `input`
from the
[`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch)
API as a prop, in addition to a few other things.

Props from the `fetch()` method are:

* `url`
* `method`: defaults to `"GET"`
* `body`
* `credentials`
* `headers`
* `mode`
* `cache`
* `redirect`
* `referrer`: defaults to `"about:client"`
* `referrerPolicy`: defaults to `""`
* `integrity`: defaults to `""`
* `keepalive`
* `signal`

To learn more about the valid options for these props, refer to the
[`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch)
documentation.

Here's an example demonstrating some of the most commonly-used props:

```jsx
<Fetch
url="/posts/2"
method="patch"
credentials="same-origin"
headers={{
'csrf-token': myCsrfToken
}}
body={JSON.stringify({ title: 'New post' })}
render={({ fetch }) => {
<button onClick={() => fetch()}>Update Post</button>;
}}
/>
```

In addition to the `fetch()` props, there are a number of other useful props.

##### `render`

The [render prop](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) of this component.
It is called with one argument, `result`, an object with the following keys:

* `fetching`: A Boolean representing whether or not a request is currently in flight for this component
* `error`: A Boolean representing if a network error occurred. Note that HTTP "error" status codes do not
cause `error` to be `true`; only failed or aborted network requests do.
* `response`: An instance of [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). The
`body` will already be read, and made available to you as `response.data`.
* `data`: An alias of `response.data`
* `fetch`: A function that makes the HTTP request. See notes below.
* `requestName`: The name of the request (see `requestName` below)

There are three common use cases for the `fetch` prop:

* For GET requests, it can allow users to refresh the data
* Anytime there is a network error, you can use this function to retry the request
* When `lazy` is `true`, you can use this to actually make the request, typically as
a result of user input

`fetch` accepts one argument: `options`. Any of the `fetch()` options described above are valid
`options`. This allows you to customize the request from within the component.

##### `lazy`

Whether or not the request will be called when the component mounts. The default value
is based on the request method that you use.

| Method | Default value |
| ------------------------ | ------------- |
| GET, HEAD, OPTIONS | `false` |
| POST, PUT, PATCH, DELETE | `true` |

##### `onResponse`

A function that is called when a request is received. Receives two arguments: `error` and `response`.

```jsx
<Fetch
url="/posts/2"
onResponse={(error, response) => {
if (error) {
console.log('Ruh roh', error);
} else {
console.log('Got a response!', response);
}
}}
render={() => {
<div>Hello</div>;
}}
/>
```

##### `transformData`

A function that is called with the data returned from the response. You can use this
hook to transform the data before it is passed into `render`.

```jsx
<Fetch
url="/posts/2"
transformData={data => {
return data.post;
}
render={({ fetching, error, response, data }) => {
<div>
{fetching && ('Loading...')}
{error && ('There was an error.')}
{!fetching && !error && response.status === 200 && (
<div>
<h1>{data.title}</h1>
<div>{data.content}</div>
</div>
)}
</div>
}}
/>
```

##### `contentType`

The content type of the response body. Defaults to `json`. Valid values are the methods
on [Body](https://developer.mozilla.org/en-US/docs/Web/API/Body).

##### `requestName`

A name to give this request, which can help with debugging purposes. The request name is
analogous to a function name in JavaScript. Although we could use anonymous functions
everywhere, we tend to give them names to help humans read and debug the code.

```jsx
<Fetch url={`/posts/${postId}`} requestName="readPost" />
```

##### `fetchPolicy`

This determines how the request interacts with the cache. For documentation, refer to the
[Apollo documentation](https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-fetchPolicy).
This prop is identical to the Apollo prop.

(The API will be listed here shortly).

---

#### `<FetchComposer />`

A component that simplifies making parallel requests.

##### `requests`

An array of `Fetch` components. Use any of the above props, but leave out `render`.

> Note: if you pass a `render` prop, it will be ignored.

##### `render`

A function that is called with the array of responses from `requests`.

### Acknowledgements

Expand Down
5 changes: 2 additions & 3 deletions src/request-composer.js → src/fetch-composer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function RequestComposer({ requests = [], render }) {
export default function FetchComposer({ requests = [], render }) {
if (typeof render !== 'function') {
return null;
}
Expand All @@ -20,7 +20,6 @@ export default function RequestComposer({ requests = [], render }) {
return render(responses);
}

// This is the index of the Request component within `requests`
const requestIndex = childrenRequests.length - 1;
const request = requests[requestIndex];

Expand All @@ -47,7 +46,7 @@ export default function RequestComposer({ requests = [], render }) {
return chainRequests(reversedRequests);
}

RequestComposer.propTypes = {
FetchComposer.propTypes = {
render: PropTypes.func,
requests: PropTypes.array
};
4 changes: 2 additions & 2 deletions src/fetch-dedupe.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function resolveRequest({ requestKey, res, err }) {
requests[requestKey] = null;
}

export default function fetchDedupe(input, init, { requestKey, type }) {
export default function fetchDedupe(input, init, { requestKey, contentType }) {
if (!requests[requestKey]) {
requests[requestKey] = [];
}
Expand All @@ -45,7 +45,7 @@ export default function fetchDedupe(input, init, { requestKey, type }) {
// The response body is a ReadableStream. ReadableStreams can only be read a single
// time, so we must handle that in a central location, here, before resolving
// the fetch.
res[type]().then(data => {
res[contentType]().then(data => {
res.data = data;
resolveRequest({ requestKey, res });
});
Expand Down
Loading