# Introduction

React Async is a utility belt for declarative promise resolution and data fetching. It makes it easy to handle asynchronous UI states, without assumptions about the shape of your data or the type of request. React Async consists of a React component and several hooks. You can use it with `fetch`, Axios or other data fetching libraries, even GraphQL.

## Rationale

React Async is different in that it tries to resolve data as close as possible to where it will be used, while using declarative syntax, using just JSX and native promises. This is in contrast to systems like Redux where you would configure any data fetching or updates on a higher (application global) level, using a special construct (actions/reducers).

React Async works well even in larger applications with multiple or nested data dependencies. It encourages loading data on-demand and in parallel at component level instead of in bulk at the route/page level. It's entirely decoupled from your routes, so it works well in complex applications that have a dynamic routing model or don't use routes at all.

React Async is promise-based, so you can resolve anything you want, not just `fetch` requests.

## Concurrent React and Suspense

The React team is currently working on a large rewrite called [Concurrent React](https://github.com/sw-yx/fresh-concurrent-react/blob/master/Intro.md#introduction-what-is-concurrent-react), previously known as "Async React". Part of this rewrite is Suspense, which is a generic way for components to suspend rendering while they load data from a cache. It can render a fallback UI while loading data, much like `<Async.Pending>`.

React Async has no direct relation to Concurrent React. They are conceptually close, but not the same. React Async is meant to make dealing with asynchronous business logic easier. Concurrent React will make those features have less impact on performance and usability. When Suspense lands, React Async will make full use of Suspense features. In fact, you can already **start using React Async right now**, and in a later update, you'll **get Suspense features for free**. In fact, React Async already has experimental support for Suspense, by passing the `suspense` option.


# Installation

You can install `react-async` from npm:

```
npm install --save react-async
```

Or if you're using Yarn:

```
yarn add react-async
```

> This package requires `react` as a peer dependency. Please make sure to install that as well. If you want to use the `useAsync` hook, you'll need `react@16.8.0` or later.

## Transpiling for legacy browsers

This project targets the latest ECMAScript version. Our packages on npm do not contain ES5 code for legacy browsers. If you need to target a browser which does not support the latest version of ECMAScript, you'll have to handle transpilation yourself. Usually this will automatically be handled by the framework you use (CRA, Next.js, Gatsby), but sometimes you may need to tweak your Webpack settings to transpile `react-async` with Babel.

To transpile `node_modules` with Babel you need to use a `babel.config.js`, for more information see [Babel's documentation](https://babeljs.io/docs/en/configuration#whats-your-use-case).

In your `webpack.config.js` make sure that the rule for `babel-loader`:

* doesn't exclude `node_modules` from matching via the `exclude` pattern;
* excludes `core-js` as it shouldn't be transpiled;
* is passed the `configFile` option pointing to the `babel.config.js` file.

```
{
  test: /\.(js|jsx)$/,
  exclude: /\/node_modules\/core-js\//,
  use: [{
    loader: 'babel-loader',
    options: {
      configFile: './babel.config.js',
      // Caching is recommended when transpiling node_modules to speed up consecutive builds
      cacheDirectory: true,
    }
  }]
}
```


# Upgrading

## Upgrade to v9

The rejection value for failed requests with `useFetch` was changed. Previously it was the Response object. Now it's an Error object with `response` property. If you are using `useFetch` and are using the `error` value, expecting it to be of type Response, you must now use `error.response` instead.

## Upgrade to v8

All standalone helper components were renamed to avoid import naming collision.

* `<Initial>` was renamed to `<IfInitial>`.
* `<Pending>` was renamed to `<IfPending>`.
* `<Fulfilled>` was renamed to `<IfFulfilled>`.
* `<Rejected>` was renamed to `<IfRejected`.
* `<Settled>` was renamed to `<IfSettled>`.

> A [codemod](https://github.com/async-library/react-async/tree/master/codemods) is available to automate the upgrade.

The return type for `run` was changed from `Promise` to `undefined`. You should now use the `promise` prop instead. This is a manual upgrade. See [`promise`](/master/api/state#promise) for details.

## Upgrade to v6

* `<Async.Pending>` was renamed to `<Async.Initial>`.
* Some of the other helpers were also renamed, but the old ones remain as alias.
* Don't forget to deal with any custom instances of `<Async>` when upgrading.

> A [codemod](https://github.com/async-library/react-async/tree/master/codemods) is available to automate the upgrade.

## Upgrade to v4

* `deferFn` now receives an `args` array as the first argument, instead of arguments to `run` being spread at the front of the arguments list. This enables better interop with TypeScript. You can use destructuring to keep using your existing variables.
* The shorthand version of `useAsync` now takes the `options` object as optional second argument. This used to be `initialValue`, but was undocumented and inflexible.


# Usage

React Async offers three primary APIs: the `useAsync` hook, the `<Async>` component and the `createInstance` factory function. Each has its unique benefits and downsides.

## As a hook

The `useAsync` hook (available [from React v16.8.0](https://reactjs.org/hooks)) offers direct access to React Async's core functionality from within your own function components:

```jsx
import { useAsync } from "react-async"

// You can use async/await or any function that returns a Promise
const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

const MyComponent = () => {
  const { data, error, isPending } = useAsync({ promiseFn: loadPlayer, playerId: 1 })
  if (isPending) return "Loading..."
  if (error) return `Something went wrong: ${error.message}`
  if (data)
    return (
      <div>
        <strong>Player data:</strong>
        <pre>{JSON.stringify(data, null, 2)}</pre>
      </div>
    )
  return null
}
```

> Using [helper components](/master/getting-started/usage#with-helper-components) can greatly improve readability of your render functions by not having to write all those conditional returns.

Or using the shorthand version:

```jsx
const MyComponent = () => {
  const { data, error, isPending } = useAsync(loadPlayer, options)
  // ...
}
```

### With `useFetch`

Because fetch is so commonly used with `useAsync`, there's a dedicated `useFetch` hook for it:

```jsx
import { useFetch } from "react-async"

const MyComponent = () => {
  const headers = { Accept: "application/json" }
  const { data, error, isPending, run } = useFetch("/api/example", { headers }, options)
  // This will setup a promiseFn with a fetch request and JSON deserialization.

  // you can later call `run` with an optional callback argument to
  // last-minute modify the `init` parameter that is passed to `fetch`
  function clickHandler() {
    run(init => ({
      ...init,
      headers: {
        ...init.headers,
        authentication: "...",
      },
    }))
  }

  // alternatively, you can also just use an object that will be spread over `init`.
  // please note that this is not deep-merged, so you might override properties present in the
  // original `init` parameter
  function clickHandler2() {
    run({ body: JSON.stringify(formValues) })
  }
}
```

`useFetch` takes the same arguments as [fetch](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) itself, as well as `options` to the underlying `useAsync` hook. The `options` object takes two special boolean properties: `defer` and `json`. These can be used to switch between `deferFn` and `promiseFn`, and enable JSON parsing. By default `useFetch` automatically uses `promiseFn` or `deferFn` based on the request method (`deferFn` for POST / PUT / PATCH / DELETE) and handles JSON parsing if the `Accept` header is set to `"application/json"`.

## As a component

The classic interface to React Async. Simply use `<Async>` directly in your JSX component tree, leveraging the render props pattern:

```jsx
import Async from "react-async"

// Your promiseFn receives all props from Async and an AbortController instance
const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

const MyComponent = () => (
  <Async promiseFn={loadPlayer} playerId={1}>
    {({ data, error, isPending }) => {
      if (isPending) return "Loading..."
      if (error) return `Something went wrong: ${error.message}`
      if (data)
        return (
          <div>
            <strong>Player data:</strong>
            <pre>{JSON.stringify(data, null, 2)}</pre>
          </div>
        )
      return null
    }}
  </Async>
)
```

> Using [helper components](/master/getting-started/usage#with-helper-components) can greatly improve readability of your render functions by not having to write all those conditional returns.

## As a factory

You can also create your own component instances, allowing you to preconfigure them with options such as default `onResolve` and `onReject` callbacks.

```jsx
import { createInstance } from "react-async"

const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

// createInstance takes a defaultOptions object and a displayName (both optional)
const AsyncPlayer = createInstance({ promiseFn: loadPlayer }, "AsyncPlayer")

const MyComponent = () => (
  <AsyncPlayer playerId={1}>
    <AsyncPlayer.Fulfilled>{player => `Hello ${player.name}`}</AsyncPlayer.Fulfilled>
  </AsyncPlayer>
)
```

## With helper components

Several [helper components](/master/getting-started/usage#helper-components) are available to improve legibility. They can be used with `useAsync` by passing in the state, or with `<Async>` by using Context. Each of these components simply enables or disables rendering of its children based on the current state.

```jsx
import { useAsync, IfPending, IfFulfilled, IfRejected } from "react-async"

const loadPlayer = async ({ playerId }, { signal }) => {
  // ...
}

const MyComponent = () => {
  const state = useAsync({ promiseFn: loadPlayer, playerId: 1 })
  return (
    <>
      <IfPending state={state}>Loading...</IfPending>
      <IfRejected state={state}>{error => `Something went wrong: ${error.message}`}</IfRejected>
      <IfFulfilled state={state}>
        {data => (
          <div>
            <strong>Player data:</strong>
            <pre>{JSON.stringify(data, null, 2)}</pre>
          </div>
        )}
      </IfFulfilled>
    </>
  )
}
```

### As compounds to `<Async>`

Each of the helper components are also available as static properties of `<Async>`. In this case you won't have to pass the state object, instead it will be automatically provided through Context.

```jsx
import Async from "react-async"

const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

const MyComponent = () => (
  <Async promiseFn={loadPlayer} playerId={1}>
    <Async.Pending>Loading...</Async.Pending>
    <Async.Fulfilled>
      {data => (
        <div>
          <strong>Player data:</strong>
          <pre>{JSON.stringify(data, null, 2)}</pre>
        </div>
      )}
    </Async.Fulfilled>
    <Async.Rejected>{error => `Something went wrong: ${error.message}`}</Async.Rejected>
  </Async>
)
```


# DevTools

React Async comes with a separate DevTools package which helps you Debug and develop your asynchronous application states. You can install it from npm:

```
npm install --save react-async-devtools
```

Or if you're using Yarn:

```
yarn add react-async-devtools
```

Then simply import it and render the`<DevTools />` component at the root of your app:

```jsx
import DevTools from "react-async-devtools"

export const Root = () => (
  <>
    <DevTools />
    <App />
  </>
)
```


# Interfaces

React Async provides several ways to use it. The classic interface is through the `<Async>` component, which is backwards compatible to React v16.3. More recent React applications will be using hooks, of which two are provided: `useAsync` and `useFetch`. Functionally, `<Async>` and `useAsync` are equivalent. `useFetch` is a special version of `useAsync` which is tied to the native `fetch` API.

React Async accepts a wide range of [configuration options](/master/api/options) and returns a set of [state props](/master/api/state). The way you use these differs slightly between the `useAsync` and `useFetch` hooks, and the `<Async>` component.

## `Async` component

```jsx
<Async {...options}>{state => ...}</Async>
```

* [`options`](/master/api/options) Configuration options
* [`state`](/master/api/state) State object

> We recommend that you pass the options individually, rather than using JSX [spread attributes](https://reactjs.org/docs/jsx-in-depth.html#spread-attributes). React Async uses [render props](https://reactjs.org/docs/render-props.html) to return its state back to you, so it can be used by other components further down the tree.

## `useAsync` hook

```javascript
const state = useAsync(options)
```

* [`state`](/master/api/state) State object
* [`options`](/master/api/options) Configuration options

> We recommend that you pass `options` as an inline object literal, and that you [destructure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) the `state` object to extract the properties you need, unless you have multiple instances in the same component.

## `useFetch` hook

```javascript
const state = useFetch(resource, init, options)
```

* [`state`](/master/api/state) State object
* [`resource`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax) The resource you want to fetch
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax) Custom request options
* [`options`](/master/api/options) Configuration options

## `createInstance`

Besides using the `Async` component directly, you can also create your own instance of it. This allows you to preload it with options, e.g. to enable global error handling.

```javascript
const CustomAsync = createInstance(defaultOptions, displayName)
```

* [`defaultOptions`](/master/api/options) Default configuration options
* `displayName` Name for this instance, used by React DevTools


# Configuration options

These can be passed in an object to `useAsync(options)`, or as props to `<Async {...options}>` and custom instances.

* [`promise`](/master/api/options#promise) An already started Promise instance.
* [`promiseFn`](/master/api/options#promisefn) Function that returns a Promise, automatically invoked.
* [`deferFn`](/master/api/options#deferfn) Function that returns a Promise, manually invoked with `run`.
* [`watch`](/master/api/options#watch) Watch a value and automatically reload when it changes.
* [`watchFn`](/master/api/options#watchfn) Watch this function and automatically reload when it returns truthy.
* [`initialValue`](/master/api/options#initialvalue) Provide initial data or error for server-side rendering.
* [`onResolve`](/master/api/options#onresolve) Callback invoked when Promise resolves.
* [`onReject`](/master/api/options#onreject) Callback invoked when Promise rejects.
* [`onCancel`](/master/api/options#oncancel) Callback invoked when a Promise is cancelled.
* [`reducer`](/master/api/options#reducer) State reducer to control internal state updates.
* [`dispatcher`](/master/api/options#dispatcher) Action dispatcher to control internal action dispatching.
* [`debugLabel`](/master/api/options#debuglabel) Unique label used in DevTools.
* [`suspense`](/master/api/options#suspense) Enable **experimental** Suspense integration.

`useFetch` additionally takes these options:

* [`defer`](/master/api/options#defer) Force the use of `deferFn` or `promiseFn`.
* [`json`](/master/api/options#json) Enable JSON parsing of the response.

## `promise`

> `Promise`

A Promise instance which has already started. It will simply add the necessary resolve/reject callbacks and set `startedAt` to the time `promise` was first provided. Changing the value of `promise` will cancel any pending promise and listen to the new one. If `promise` is initially undefined, the React Async state will be `pending`.

> Note that `reload` will not do anything when using `promise`. Use `promiseFn` instead.

## `promiseFn`

> `function(props: Object, controller: AbortController): Promise`

A function that returns a promise. It is automatically invoked in `componentDidMount` and `componentDidUpdate`. The function receives all component props (or options) and an AbortController instance as arguments.

> Be aware that updating `promiseFn` will trigger it to cancel any pending promise and load the new promise. Passing an inline (arrow) function will cause it to change and reload on every render of the parent component. You can avoid this by defining the `promiseFn` value **outside** of the render method. If you need to pass variables to the `promiseFn`, pass them as additional props to `<Async>`, as `promiseFn` will be invoked with these props. Alternatively you can use `useCallback` or [memoize-one](https://github.com/alexreardon/memoize-one) to avoid unnecessary updates.

## `deferFn`

> `function(args: any[], props: Object, controller: AbortController): Promise`

A function that returns a promise. This is invoked only by manually calling `run(...args)`. Receives the same arguments as `promiseFn`, as well as any arguments to `run` which are passed through as an array. The `deferFn` is commonly used to send data to the server following a user action, such as submitting a form. You can use this in conjunction with `promiseFn` to fill the form with existing data, then updating it on submit with `deferFn`.

> Be aware that when using both `promiseFn` and `deferFn`, the shape of their fulfilled value should match, because they both update the same `data`.

## `watch`

> `any`

Watches this property through `componentDidUpdate` and re-runs the `promiseFn` when the value changes, using a simple reference check (`oldValue !== newValue`). If you need a more complex update check, use `watchFn` instead.

## `watchFn`

> `function(props: Object, prevProps: Object): boolean | any`

Re-runs the `promiseFn` when this callback returns truthy (called on every update). Any default props specified by `createInstance` are available too.

## `initialValue`

> `any | Error`

Initial state for `data` or `error` (if instance of Error); useful for server-side rendering. When an `initialValue` is provided, the `promiseFn` will not be invoked on first render. Instead, `status` will be immediately set to `fulfilled` or `rejected` and your components will render accordingly. If you want to trigger the `promiseFn` regardless, you can call `reload()` or use the `watch` or `watchFn` option.

> Note that `onResolve` or `onReject` is not invoked in this case and no `promise` prop will be created.

## `onResolve`

> `function(data: any): void`

Callback function invoked when a promise resolves, receives data as argument.

## `onReject`

> `function(reason: Error): void`

Callback function invoked when a promise rejects, receives rejection reason (error) as argument.

## `onCancel`

> `function(): void`

Callback function invoked when a promise is cancelled, either manually using `cancel()` or automatically due to props changes or unmounting.

## `reducer`

> `function(state: any, action: Object, internalReducer: function(state: any, action: Object))`

State reducer to take full control over state updates by wrapping the [internal reducer](https://github.com/async-library/react-async/blob/master/src/reducer.js). It receives the current state, the dispatched action and the internal reducer. You probably want to invoke the internal reducer at some point.

> This is a power feature which loosely follows the [state reducer pattern](https://kentcdodds.com/blog/the-state-reducer-pattern). It allows you to control state changes by intercepting actions before they are handled, or by overriding or enhancing the reducer itself.

## `dispatcher`

> `function(action: Object, internalDispatch: function(action: Object), props: Object)`

Action dispatcher to take full control over action dispatching by wrapping the internal dispatcher. It receives the original action, the internal dispatcher and all component props (or options). You probably want to invoke the internal dispatcher at some point.

> This is a power feature similar to the [state reducer pattern](https://kentcdodds.com/blog/the-state-reducer-pattern). It allows you to control state changes by intercepting actions before they are dispatched, to dispatch additional actions, possibly later in time.

## `debugLabel`

> `string`

A unique label to describe this React Async instance, used in React DevTools (through `useDebugValue`) and React Async DevTools.

## `suspense`

> `boolean`

Enables **experimental** Suspense integration. This will make React Async throw a promise while loading, so you can use Suspense to render a fallback UI, instead of using `<IfPending>`. Suspense differs in 2 main ways:

* `<Suspense>` should be an ancestor of your Async component, instead of a descendant. It can be anywhere up in the

  component hierarchy.
* You can have a single `<Suspense>` wrap multiple Async components, in which case it will render the fallback UI until

  all promises are settled.

> Note that the way Suspense is integrated right now may change. Until Suspense for data fetching is officially released, we may make breaking changes to its integration in React Async in a minor or patch release. Among other things, we'll probably add a cache of sorts.

## `defer`

> `boolean`

Enables the use of `deferFn` if `true`, or enables the use of `promiseFn` if `false`. By default this is automatically chosen based on the request method (`deferFn` for POST / PUT / PATCH / DELETE, `promiseFn` otherwise).

## `json`

> `boolean`

Enables or disables JSON parsing of the response body. By default this is automatically enabled if the `Accept` header is set to `"application/json"`.


# State properties

These are returned in an object by `useAsync()` or provided by `<Async>` as render props to the `children` function:

* [`data`](/master/api/state#data) Last resolved promise value, maintained when new error arrives.
* [`error`](/master/api/state#error) Rejected promise reason, cleared when new data arrives.
* [`value`](/master/api/state#value) The value of `data` or `error`, whichever was last updated.
* [`initialValue`](/master/api/state#initialvalue) The data or error that was provided through the `initialValue` prop.
* [`startedAt`](/master/api/state#startedat) When the current/last promise was started.
* [`finishedAt`](/master/api/state#finishedat) When the last promise was fulfilled or rejected.
* [`status`](/master/api/state#status) One of: `initial`, `pending`, `fulfilled`, `rejected`.
* [`isInitial`](/master/api/state#isinitial) true when no promise has ever started, or one started but was cancelled.
* [`isPending`](/master/api/state#ispending) true when a promise is currently awaiting settlement. Alias: `isLoading`
* [`isFulfilled`](/master/api/state#isfulfilled) true when the last promise was fulfilled. Alias: `isResolved`
* [`isRejected`](/master/api/state#isrejected) true when the last promise was rejected.
* [`isSettled`](/master/api/state#issettled) true when the last promise was fulfilled or rejected (not initial or pending).
* [`counter`](/master/api/state#counter) The number of times a promise was started.
* [`promise`](/master/api/state#promise) A reference to the internal wrapper promise, which can be chained on.
* [`run`](/master/api/state#run) Invokes the `deferFn`.
* [`reload`](/master/api/state#reload) Re-runs the promise when invoked, using any previous arguments.
* [`cancel`](/master/api/state#cancel) Cancel any pending promise.
* [`setData`](/master/api/state#setdata) Sets `data` to the passed value, unsets `error` and cancels any pending promise.
* [`setError`](/master/api/state#seterror) Sets `error` to the passed value and cancels any pending promise.

## `data`

> `any`

Last resolved promise value, maintained when new error arrives.

## `error`

> `Error`

Rejected promise reason, cleared when new data arrives.

## `value`

> `any | Error`

The data or error that was last provided (either through `initialValue` or by settling a promise).

## `initialValue`

> `any | Error`

The data or error that was originally provided through the `initialValue` prop.

## `startedAt`

> `Date`

Tracks when the current/last promise was started.

## `finishedAt`

> `Date`

Tracks when the last promise was resolved or rejected.

## `status`

> `string`

One of: `initial`, `pending`, `fulfilled`, `rejected`. These are available for import as `statusTypes`.

## `isInitial`

> `boolean`

`true` while no promise has started yet, or one was started but cancelled.

## `isPending`

> `boolean`

`true` while a promise is pending (loading), `false` otherwise.

Alias: `isLoading`

## `isFulfilled`

> `boolean`

`true` when the last promise was fulfilled (resolved to a value).

Alias: `isResolved`

## `isRejected`

> `boolean`

`true` when the last promise was rejected.

## `isSettled`

> `boolean`

`true` when the last promise was either fulfilled or rejected (i.e. not initial or pending)

## `counter`

> `number`

The number of times a promise was started.

## `promise`

> `Promise`

A reference to the internal wrapper promise created when starting a new promise (either automatically or by invoking `run` / `reload`). It fulfills or rejects along with the provided `promise` / `promiseFn` / `deferFn`. Useful as a chainable alternative to the `onResolve` / `onReject` callbacks.

Warning! If you chain on `promise`, you MUST provide a rejection handler (e.g. `.catch(...)`). Otherwise React will throw an exception and crash if the promise rejects.

## `run`

> `function(...args: any[]): void`

Runs the `deferFn`, passing any arguments provided as an array.

When used with `useFetch`, `run` has several overloaded signatures:

> `function(override: OverrideParams | (params: OverrideParams) => OverrideParams): void`
>
> `function(event: SyntheticEvent | Event): void`
>
> `function(): void`

Where `type OverrideParams = { resource?: RequestInfo } & Partial<RequestInit>`.

This way you can run the `fetch` request with custom `resource` and `init`. If `override` is an object it will be spread over the default `resource` and `init` for `fetch`. If it's a function it will be invoked with the params defined with `useFetch`, and should return an `override` object. This way you can either extend or override the value of `resource` and `init`, for example to change the URL or set custom request headers.

## `reload`

> `function(): void`

Re-runs the promise when invoked, using the previous arguments.

## `cancel`

> `function(): void`

Cancels the currently pending promise by ignoring its result and calls `abort()` on the AbortController.

## `setData`

> `function(data: any, callback?: () => void): any`

Function that sets `data` to the passed value, unsets `error` and cancels any pending promise. Takes an optional callback which is invoked after the state update is completed. Returns the data to enable chaining.

## `setError`

> `function(error: Error, callback?: () => void): Error`

Function that sets `error` to the passed value and cancels any pending promise. Takes an optional callback which is invoked after the state update is completed. Returns the error to enable chaining.


# Helper components

React Async provides several helper components that make your JSX more declarative and less cluttered. They don't have to be direct children of `<Async>` and you can use the same component several times.

## `<IfInitial>` / `<Async.Initial>`

Renders only while the deferred promise is still waiting to be run, or you have not provided any promise.

### Props

* `children` `function(state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `persist` `boolean` Show until we have data, even while loading or when an error occurred. By default it hides as soon

  as the promise starts loading.

### Examples

```jsx
const state = useAsync(...)
return (
  <IfInitial state={state}>
    <p>This text is only rendered while `run` has not yet been invoked on `deferFn`.</p>
  </IfInitial>
)
```

```jsx
<Async deferFn={deferFn}>
  <Async.Initial>
    <p>This text is only rendered while `run` has not yet been invoked on `deferFn`.</p>
  </Async.Initial>
</Async>
```

```jsx
<Async.Initial persist>
  {({ error, isPending, run }) => (
    <div>
      <p>This text is only rendered while the promise has not fulfilled yet.</p>
      <button onClick={run} disabled={!isPending}>
        Run
      </button>
      {error && <p>{error.message}</p>}
    </div>
  )}
</Async.Initial>
```

## `<IfPending>` / `<Async.Pending>`

This component renders only while the promise is pending (loading / unsettled).

Alias: `<Async.Loading>`

### Props

* `children` `function(state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `initial` `boolean` Show only on initial load (when `data` is `undefined`).

### Examples

```jsx
const state = useAsync(...)
return (
  <IfPending state={state}>
    <p>This text is only rendered while performing the initial load.</p>
  </IfPending>
)
```

```jsx
<Async.Pending initial>
  <p>This text is only rendered while performing the initial load.</p>
</Async.Pending>
```

```jsx
<Async.Pending>{({ startedAt }) => `Loading since ${startedAt.toISOString()}`}</Async.Pending>
```

## `<IfFulfilled>` / `<Async.Fulfilled>`

This component renders only when the promise is fulfilled (resolved to a value, could be `undefined`).

Alias: `<Async.Resolved>`

### Props

* `children` `function(data: any, state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `persist` `boolean` Show old data while loading new data. By default it hides as soon as a new promise starts.

### Examples

```jsx
const state = useAsync(...)
return (
  <IfFulfilled state={state}>
    {data => <pre>{JSON.stringify(data)}</pre>}
  </IfFulfilled>
)
```

```jsx
<Async.Fulfilled persist>{data => <pre>{JSON.stringify(data)}</pre>}</Async.Fulfilled>
```

```jsx
<Async.Fulfilled>
  {(data, { finishedAt }) => `Last updated ${finishedAt.toISOString()}`}
</Async.Fulfilled>
```

## `<IfRejected>` / `<Async.Rejected>`

This component renders only when the promise is rejected.

### Props

* `children` `function(error: Error, state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `persist` `boolean` Show old error while loading new data. By default it hides as soon as a new promise starts.


# Async components

The most common use case for React Async is data fetching. In single-page applications it's very common to dynamically load some data from a backend. React Async makes it incredibly easy to set this up, without having to worry about the details.

The mental model of React Async is component-first. Rather than loading data high up in your application and passing it down to a component for display, you perform the data loading at the component level. Such a component is called an async component. An async component can render its state in a meaningful way like any other component, or be logic-only. In that case it doesn't render any UI but instead passes its state down to its children. Such separation of concerns is good practice.

## Creating an async component with `useFetch`

The easiest way to create an async component for data fetching is through the [`useFetch` hook](/master/api/interfaces#usefetch-hook):

```jsx
import React from "react"
import { useFetch } from "react-async"

const Person = ({ id }) => {
  const { data, error } = useFetch(`https://swapi.co/api/people/${id}/`, {
    headers: { accept: "application/json" },
  })
  if (error) return error.message
  if (data) return `Hi, my name is ${data.name}!`
  return null
}

const App = () => {
  return <Person id={1} />
}
```

## More flexibility with `useAsync`

For most data fetching needs, `useFetch` is sufficient. However, sometimes you may want to take full control, for example if you want to combine multiple requests. In this case you can use the [`useAsync` hook](/master/api/interfaces#useasync-hook).

The core concept of `useAsync` (and React Async in general), is the [`promiseFn`](/master/api/options#promisefn): a function that returns a `Promise`. It's the fundamental concept for modelling asynchronous operations. It enables React Async to take control over scheduling, the Promise lifecycle and things like (re)starting an operation on user action or other changes. We've deliberately chosen the `Promise` as our primitive, because it's natively supported and has various utility methods like `Promise.all`. That's also why you'll find our terminology closely follows the Promise [states and fates](https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md).

The above example, written with `useAsync`, would look like this:

```jsx
import React from "react"
import { useAsync } from "react-async"

const fetchPerson = async ({ id }, { signal }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal })
  if (!response.ok) throw new Error(response.status)
  return response.json()
}

const Person = ({ id }) => {
  const { data, error } = useAsync({ promiseFn: fetchPerson, id })
  if (error) return error.message
  if (data) return `Hi, my name is ${data.name}!`
  return null
}

const App = () => {
  return <Person id={1} />
}
```

Notice the incoming parameters to `fetchPerson`. The `promiseFn` will be invoked with a `props` object and an `AbortController`. `props` are the options you passed to `useAsync`, which is why you can access the `id` property using [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring). The `AbortController` is created by React Async to enable [abortable fetch](https://developers.google.com/web/updates/2017/09/abortable-fetch), so the underlying request will be aborted when the promise is cancelled (e.g. when a new one starts or we leave the page). We have to pass its `AbortSignal` down to `fetch` in order to wire this up.


# Separating view and logic

It's generally good practice to separate view components from logic components. Async components should preferably be logic-only. That means they don't render anything by themselves. Instead you can use the [render props](https://reactjs.org/docs/render-props.html) pattern to pass down the async state:

```jsx
import React from "react"
import { useAsync } from "react-async"

const fetchPerson = async ({ id }, { signal }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal })
  if (!response.ok) throw new Error(response.statusText)
  return response.json()
}

const Person = ({ id }) => {
  const state = useAsync({ promiseFn: fetchPerson, id })
  return children(state)
}

const App = () => {
  return (
    <Person id={1}>
      {({ isPending, data, error }) => {
        if (isPending) return "Loading..."
        if (error) return <ErrorMessage {...error} />
        if (data) return <Greeting {...data} />
        return null
      }}
    </Person>
  )
}
```

> `ErrorMessage` and `Greeting` would be separate view components defined elsewhere.

## Cleaning up the JSX

You'll notice the render props pattern is very powerful, but can also lead to code that's hard to read and understand. To make your JSX more declarative and less cluttered, you can use the [`<Async>`](/master/api/interfaces#async-component) component and its [state helpers](/master/api/helpers). These take away the need for `if/else` statements and `return` keywords in your JSX.

```jsx
import React from "react"
import Async from "react-async"

const fetchPerson = async ({ id }, { signal }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal })
  if (!response.ok) throw new Error(response.statusText)
  return response.json()
}

const App = () => {
  return (
    <Async promiseFn={fetchPerson} id={1}>
      <Async.Pending>Loading...</Async.Pending>
      <Async.Rejected>{error => <ErrorMessage {...error} />}</Async.Rejected>
      <Async.Fulfilled>{data => <Greeting {...data} />}</Async.Fulfilled>
    </Async>
  )
}
```

You should know that these helper components do not have to be direct children of the `<Async>` component. Because they are automatically wired up using [Context](https://reactjs.org/docs/context.html), they can be placed anywhere down the component tree, so long as they are descendants. You can also use helpers of the same type, multiple times.

Stand-alone versions of `<Async.Pending>` and the like are also available. However, these must be wired up manually by passing the `state` prop and are therefore only really useful when combined with one of the async hooks.


# Async actions

Fetching data for display alone isn't sufficient for most applications. You'll often also want to submit data back to the server, or handle other types of asynchronous actions. To enable this, React Async has the concept of a [`deferFn`](/master/api/options#deferfn).

Like `promiseFn`, a `deferFn` is a function that returns a Promise. The difference is that `deferFn` will not be automatically invoked by React Async when rendering the component. Instead it will have to be triggered by calling the [`run`](/master/api/state#run) function provided by React Async.

```jsx
import React, { useState } from "react"
import { useAsync } from "react-async"

const subscribe = ([email], props, { signal }) =>
  fetch("/newsletter", { method: "POST", body: JSON.stringify({ email }), signal })

const NewsletterForm = () => {
  const { isPending, error, run } = useAsync({ deferFn: subscribe })
  const [email, setEmail] = useState("")

  const handleSubmit = event => {
    event.preventDefault()
    run(email)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={event => setEmail(event.target.value)} />
      <button type="submit" disabled={isPending}>
        Subscribe
      </button>
      {error && <p>{error.message}</p>}
    </form>
  )
}
```

As you can see, the `deferFn` is invoked with 3 arguments: `args`, `props` and the AbortController. `args` is an array representing the arguments that were passed to `run`. In this case we passed the `email`, so we can extract that from the `args` array at the first index using [array destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Array_destructuring) and pass it along to our `fetch` request.

## Sending data with `useFetch`

The above example can be simplified when we rely on [`useFetch`](/master/api/interfaces#usefetch-hook) instead of constructing the request manually.

```jsx
import React, { useState } from "react"
import { useFetch } from "react-async"

const NewsletterForm = () => {
  const { isPending, error, run } = useFetch("/newsletter", { method: "POST" })
  const [email, setEmail] = useState("")

  const handleSubmit = event => {
    event.preventDefault()
    run({ body: JSON.stringify({ email }) })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={event => setEmail(event.target.value)} />
      <button type="submit" disabled={isPending}>
        Subscribe
      </button>
      {error && <p>{error.message}</p>}
    </form>
  )
}
```

The [`run`](/master/api/state#run) function for `useFetch` is a little special because it allows you to override the request's resource and other params. This way you can pass in the body, add dynamic headers or override the URL.


# Optimistic updates

A powerful pattern to improve your app's perceived performance is optimistic updates. When building an async action, you might be able to predict the outcome of the operation. If so, you can implement optimistic updates by proactively setting the `data` to the predicted value, when starting the async action. Once the action completes, it will update `data` to the actual value, probably the same value as predicted.

The following example uses both `promiseFn` and `deferFn` along with [`setData`](/master/api/state#setdata) to implement optimistic updates.

```jsx
import Async from "react-async"

const getAttendance = () => fetch("/attendance").then(() => true, () => false)
const updateAttendance = ([attend]) =>
  fetch("/attendance", { method: attend ? "POST" : "DELETE" }).then(() => attend, () => !attend)

const AttendanceToggle = () => (
  <Async promiseFn={getAttendance} deferFn={updateAttendance}>
    {({ isPending, data: isAttending, run, setData }) => (
      <Toggle
        on={isAttending}
        onClick={() => {
          setData(!isAttending)
          run(!isAttending)
        }}
        disabled={isPending}
      />
    )}
  </Async>
)
```

Here we have a switch to toggle attentance for an event. Clicking the toggle will most likely succeed, so we can predict the value it will have after completion (because we're just flipping a boolean).

Notice that React Async accepts both a `promiseFn` and a `deferFn` at the same time. This allows you to combine data fetching with performing actions. A typical example of where this is useful is with forms, where you first want to populate the fields with current values from the database, and send the new values back when submitting the form. Do note that `promiseFn` and `deferFn` operate on the same `data`, so they should both resolve to a similar kind of value.


# Server-side rendering

There's a good chance you're using React with Server-side rendering (SSR), as many applications require this to be successful. If you happen to be using Next.js, it's really easy to integrate React Async. The crux is in setting a [`initialValue`](/master/api/options#initialvalue), which is fetched server-side for initial page loads and passed along through rehydration.

```jsx
import fetch from "isomorphic-unfetch"

const fetchPerson = async ({ id }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`)
  if (!response.ok) throw new Error(response.status)
  return response.json()
}

const Person = ({ id, person }) => (
  <Async promiseFn={fetchPerson} initialValue={person} id={id}>
    <Async.Pending>Loading...</Async.Pending>
    <Async.Rejected>{error => <ErrorMessage {...error} />}</Async.Rejected>
    <Async.Fulfilled>{data => <Greeting {...data} />}</Async.Fulfilled>
  </Async>
)

Person.getInitialProps = async ({ req }) => {
  const id = req.params.id
  const person = await fetchPerson({ id })
  return { id, person }
}
```

If React Async is provided an `initialValue`, it will not invoke the `promiseFn` on mount. Instead it will use the `initialValue` to immediately set `data` or `error`, and render accordingly.


# Introduction

Thanks for your interest in improving React Async! Contributions of any kind are welcome. Please refer to this guide before opening an issue or pull request.

This repo relies on Yarn workspaces, so you should [install](https://yarnpkg.com/en/docs/install) and use `yarn@1.3.2` or higher as the package manager for this project.

## Development guide

Please have the ***latest*** stable versions of the following on your machine

* node
* yarn

### Initial setup

To start working on React Async, clone the repo and bootstrap the project:

```bash
git clone https://github.com/async-library/react-async.git
cd react-async
yarn && yarn bootstrap && yarn test
```

Note that all work is done against the `next` branch, we only merge to `master` when doing a release.

### Working with Storybook

We use Storybook as a development environment, particularly for the DevTools. Spin it up using:

```bash
yarn start:storybook
```

This should open up Storybook in a browser at <http://localhost:6006/> Run it side-by-side with `yarn test --watch` during development. See [Testing](/master/contributing/introduction#testing).

### Linting

Use `yarn lint` to verify your code style before committing. It's highly recommended to install the Prettier and ESLint plugins for your IDE. Travis CI will fail your build on lint errors. Configure VS Code with the following settings:

```
"eslint.autoFixOnSave": true,
"eslint.packageManager": "yarn",
"eslint.options": {
  "cache": true,
  "cacheLocation": ".cache/eslint",
  "extensions": [".js", ".jsx", ".mjs", ".json", ".ts", ".tsx"]
},
"eslint.validate": [
  "javascript",
  "javascriptreact",
  {"language": "typescript", "autoFix": true },
  {"language": "typescriptreact", "autoFix": true }
],
"eslint.alwaysShowStatus": true
```

This should enable auto-fix for all source files, and give linting warnings and errors within your editor.

### Testing

Use the following command to test all packages in watch mode. Refer to the [Jest CLI options](https://jestjs.io/docs/en/cli#options) for details.

```bash
yarn test:watch
```

In general, this is sufficient during development. Travis CI will apply a more rigorous set of tests.

#### Testing for compatibility

```bash
yarn test:compat
```

This runs all tests using various versions of `react` and `react-dom`, to check for compatibility with older/newer versions of React. This is what CircleCI and Travis run.

### Working with the examples

In the `examples` folder, you will find sample React applications that use React Async in various ways with various other libraries. Please add a new example when introducing a major new feature. Make sure to add it to `now.json` so it is automatically deployed when merged to `master`.

To run sample examples on your local environments

```bash
yarn build:examples
yarn test:examples
yarn start:examples
```

### Resolving issues

Sometimes your dependencies might end up in a weird state, causing random issues, especially when working with the examples. In this case it often helps to run `yarn clean -y && yarn bootstrap`. This will delete `node_modules` from all packages/examples and do a clean install.


# Setting up

## Prerequisites

In order to develop React Async on your local machine, you'll need `git`, `node` and `yarn`.

### Git

To clone the repository, commit your changes and push them upstream, you'll need to have `git` [installed](https://www.atlassian.com/git/tutorials/install-git).

### Node.js

As a JavaScript project, we rely heavily on Node.js. It's recommended to use a version manager such as [fnm](https://github.com/Schniz/fnm) for Mac / Linux or [nvm-windows](https://github.com/coreybutler/nvm-windows) for Windows to install the latest Node.js with.

### Yarn

This repo relies on Yarn workspaces, so you should [install](https://yarnpkg.com/en/docs/install) and use `yarn@1.3.2` or higher as the package manager for this project.

## Project setup

To start working on React Async, clone the repository and bootstrap the project by running the following commands one-by-one:

```bash
git clone https://github.com/async-library/react-async.git
cd react-async
yarn install
yarn bootstrap
yarn test
```

This should install all dependencies, build and link the react-async and react-async-devtools packages to the examples, and finally run the unit tests. In the end it should succeed with a message (numbers may change):

```
Test Suites: 6 passed, 6 total
Tests:       136 passed, 136 total
```

> Note that all work is done against the `next` branch, we only merge to `master` when doing a release.

## Editor setup

We recommend using [Visual Studio Code](https://code.visualstudio.com/) with the following extensions:

* [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
* [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
* [DeepScan](https://marketplace.visualstudio.com/items?itemName=DeepScan.vscode-deepscan)
* [Oceanic Plus](https://marketplace.visualstudio.com/items?itemName=marcoms.oceanic-plus)

Make sure to enable `editor.formatOnSave`, so Prettier will automatically apply the right code style. For the full immersive experience you can also install and use the [Overpass Mono](https://overpassfont.org/) font.


# Development

React Async is a library without visual parts. Only the DevTools have a user interface you can spin up in a browser. Therefore the development workflow for the core library might be different from what you're used to. Generally, we use a TDD approach:

* Write a unit test for the new feature or bug you want to fix. Sometimes you can just extend an existing test.
* Fix the test by implementing the feature or bugfix. Now all tests should pass.
* Optionally refactor the code for performance, readability and style. Probably this will come up during PR review.

We use the GitHub pull request workflow. In practice this means your workflow looks like this:

* Fork the repo (or pull the latest upstream) under your own account.
* Make your changes, commit and push them. We don't enforce any commit message format.
* Open a pull request on the main repository against the `next` branch. Make sure to follow the template.
* We'll review your PR and will probably ask for some changes.
* Once ready, we'll merge your PR.
* Your changes will be in the next release.

## Working with Storybook

We use Storybook as a development environment for the DevTools. Spin it up using:

```bash
yarn start:storybook
```

This should open up Storybook in a browser at <http://localhost:6006/> Run it side-by-side with `yarn test --watch` during development. See [Testing](/master/contributing/development#testing).

## Working with the examples

In the `examples` folder, you will find sample React applications that use React Async in various ways with various other libraries. Please add a new example when introducing a major new feature. Make sure to add it to `now.json` so it is automatically deployed when merged to `master`.

To run sample examples on your local environments

```bash
yarn build:examples
yarn test:examples
yarn start:examples
```

## Resolving issues

Sometimes your dependencies might end up in a weird state, causing random issues, especially when working with the examples. In this case it often helps to run `yarn clean -y && yarn bootstrap`. This will delete `node_modules` from all packages/examples and do a clean install.


# Testing

Use the following command to test all packages in watch mode. Refer to the [Jest CLI options](https://jestjs.io/docs/en/cli#options) for details.

```bash
yarn test:watch
```

In general, this is sufficient during development. CircleCI and Travis will eventually apply a more rigorous set of tests against your pull request, including the ones below.

## Testing the examples

Because React Async is only a piece in a bigger puzzle, testing for integration with other libraries is very important. You can run the tests for all examples against your local changes with the following command:

```bash
yarn test:examples
```

If you want to add integration tests for compatibility with another library, please add an example for it.

## Testing for compatibility

```bash
yarn test:compat
```

This runs all tests using various versions of `react` and `react-dom`, to check for compatibility with older/newer versions of React. This is what CircleCI and Travis run.

## Linting

Use `yarn lint` to verify your code style before committing. It's highly recommended to install the Prettier and ESLint plugins for your IDE. CircleCI and Travis will fail your build on lint errors.


# Releasing

All ongoing development is done on the `next` branch. When preparing for a release, we'll create a `release` branch which will eventually be merged into `master`. This way, what's on `master` is always what's published on `npm`.

Release management is currently a manual process, to be performed by core team members only. Here's the process:

1. Create a `release` branch, usually based on `next`.
2. Open a pull request for `release` -> `master`
3. Write the release notes in the PR description.
4. Decide on the version number, taking care to follow semver. Do a pre-release before doing the actual release.
5. Run `yarn bump` to increment the version number in all `package.json` files as well as `lerna.json`.
6. Commit the version change as "Release vX.X.X" (using the correct version number).
7. Tag the release commit with `git tag vX.X.X` (using the correct version number).
8. Push the release commit AND tag: `git push --follow-tags`
9. Publish each package (in `./packages`) to npm using the script below.
10. Create a new release on GitHub and copy the release notes there.

```
yarn build:packages
cd packages/react-async
npm publish pkg
cd ../react-async-devtools
npm publish pkg
```

Take care to publish the `pkg` directory!


# Introduction

React Async is a utility belt for declarative promise resolution and data fetching. It makes it easy to handle asynchronous UI states, without assumptions about the shape of your data or the type of request. React Async consists of a React component and several hooks. You can use it with `fetch`, Axios or other data fetching libraries, even GraphQL.

## Rationale

React Async is different in that it tries to resolve data as close as possible to where it will be used, while using declarative syntax, using just JSX and native promises. This is in contrast to systems like Redux where you would configure any data fetching or updates on a higher (application global) level, using a special construct (actions/reducers).

React Async works well even in larger applications with multiple or nested data dependencies. It encourages loading data on-demand and in parallel at component level instead of in bulk at the route/page level. It's entirely decoupled from your routes, so it works well in complex applications that have a dynamic routing model or don't use routes at all.

React Async is promise-based, so you can resolve anything you want, not just `fetch` requests.

## Concurrent React and Suspense

The React team is currently working on a large rewrite called [Concurrent React](https://reactjs.org/docs/concurrent-mode-intro.html), previously known as "Async React". Part of this rewrite is Suspense, which is a generic way for components to suspend rendering while they load data from a cache. It can render a fallback UI while loading data, much like `<Async.Pending>`.

React Async has no direct relation to Concurrent React. They are conceptually close, but not the same. React Async is meant to make dealing with asynchronous business logic easier. Concurrent React will make those features have less impact on performance and usability. When Suspense lands, React Async will make full use of Suspense features. In fact, you can already **start using React Async right now**, and in a later update, you'll **get Suspense features for free**. In fact, React Async already has experimental support for Suspense, by passing the `suspense` option.


# Installation

You can install `react-async` from npm:

```
npm install --save react-async
```

Or if you're using Yarn:

```
yarn add react-async
```

> This package requires `react` as a peer dependency. Please make sure to install that as well. If you want to use the `useAsync` hook, you'll need `react@16.8.0` or later.

## Transpiling for legacy browsers

This project targets the latest ECMAScript version. Our packages on npm do not contain ES5 code for legacy browsers. If you need to target a browser which does not support the latest version of ECMAScript, you'll have to handle transpilation yourself. Usually this will automatically be handled by the framework you use (CRA, Next.js, Gatsby), but sometimes you may need to tweak your Webpack settings to transpile `react-async` with Babel.

To transpile `node_modules` with Babel you need to use a `babel.config.js`, for more information see [Babel's documentation](https://babeljs.io/docs/en/configuration#whats-your-use-case).

In your `webpack.config.js` make sure that the rule for `babel-loader`:

* doesn't exclude `node_modules` from matching via the `exclude` pattern;
* excludes `core-js` as it shouldn't be transpiled;
* is passed the `configFile` option pointing to the `babel.config.js` file.

```
{
  test: /\.(js|jsx)$/,
  exclude: /\/node_modules\/core-js\//,
  use: [{
    loader: 'babel-loader',
    options: {
      configFile: './babel.config.js',
      // Caching is recommended when transpiling node_modules to speed up consecutive builds
      cacheDirectory: true,
    }
  }]
}
```


# Upgrading

## Upgrade to v9

The rejection value for failed requests with `useFetch` was changed. Previously it was the Response object. Now it's an Error object with `response` property. If you are using `useFetch` and are using the `error` value, expecting it to be of type Response, you must now use `error.response` instead.

## Upgrade to v8

All standalone helper components were renamed to avoid import naming collision.

* `<Initial>` was renamed to `<IfInitial>`.
* `<Pending>` was renamed to `<IfPending>`.
* `<Fulfilled>` was renamed to `<IfFulfilled>`.
* `<Rejected>` was renamed to `<IfRejected`.
* `<Settled>` was renamed to `<IfSettled>`.

> A [codemod](https://github.com/async-library/react-async/tree/master/codemods) is available to automate the upgrade.

The return type for `run` was changed from `Promise` to `undefined`. You should now use the `promise` prop instead. This is a manual upgrade. See [`promise`](/api/state#promise) for details.

## Upgrade to v6

* `<Async.Pending>` was renamed to `<Async.Initial>`.
* Some of the other helpers were also renamed, but the old ones remain as alias.
* Don't forget to deal with any custom instances of `<Async>` when upgrading.

> A [codemod](https://github.com/async-library/react-async/tree/master/codemods) is available to automate the upgrade.

## Upgrade to v4

* `deferFn` now receives an `args` array as the first argument, instead of arguments to `run` being spread at the front of the arguments list. This enables better interop with TypeScript. You can use destructuring to keep using your existing variables.
* The shorthand version of `useAsync` now takes the `options` object as optional second argument. This used to be `initialValue`, but was undocumented and inflexible.


# Usage

React Async offers three primary APIs: the `useAsync` hook, the `<Async>` component and the `createInstance` factory function. Each has its unique benefits and downsides.

## As a hook

The `useAsync` hook (available [from React v16.8.0](https://reactjs.org/hooks)) offers direct access to React Async's core functionality from within your own function components:

```jsx
import { useAsync } from "react-async"

// You can use async/await or any function that returns a Promise
const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

const MyComponent = () => {
  const { data, error, isPending } = useAsync({ promiseFn: loadPlayer, playerId: 1 })
  if (isPending) return "Loading..."
  if (error) return `Something went wrong: ${error.message}`
  if (data)
    return (
      <div>
        <strong>Player data:</strong>
        <pre>{JSON.stringify(data, null, 2)}</pre>
      </div>
    )
  return null
}
```

> Using [helper components](/getting-started/usage#with-helper-components) can greatly improve readability of your render functions by not having to write all those conditional returns.

Or using the shorthand version:

```jsx
const MyComponent = () => {
  const { data, error, isPending } = useAsync(loadPlayer, options)
  // ...
}
```

### With `useFetch`

Because fetch is so commonly used with `useAsync`, there's a dedicated `useFetch` hook for it:

```jsx
import { useFetch } from "react-async"

const MyComponent = () => {
  const headers = { Accept: "application/json" }
  const { data, error, isPending, run } = useFetch("/api/example", { headers }, options)
  // This will setup a promiseFn with a fetch request and JSON deserialization.

  // you can later call `run` with an optional callback argument to
  // last-minute modify the `init` parameter that is passed to `fetch`
  function clickHandler() {
    run(init => ({
      ...init,
      headers: {
        ...init.headers,
        authentication: "...",
      },
    }))
  }

  // alternatively, you can also just use an object that will be spread over `init`.
  // please note that this is not deep-merged, so you might override properties present in the
  // original `init` parameter
  function clickHandler2() {
    run({ body: JSON.stringify(formValues) })
  }
}
```

`useFetch` takes the same arguments as [fetch](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) itself, as well as `options` to the underlying `useAsync` hook. The `options` object takes two special boolean properties: `defer` and `json`. These can be used to switch between `deferFn` and `promiseFn`, and enable JSON parsing. By default `useFetch` automatically uses `promiseFn` or `deferFn` based on the request method (`deferFn` for POST / PUT / PATCH / DELETE) and handles JSON parsing if the `Accept` header is set to `"application/json"`.

## As a component

The classic interface to React Async. Simply use `<Async>` directly in your JSX component tree, leveraging the render props pattern:

```jsx
import Async from "react-async"

// Your promiseFn receives all props from Async and an AbortController instance
const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

const MyComponent = () => (
  <Async promiseFn={loadPlayer} playerId={1}>
    {({ data, error, isPending }) => {
      if (isPending) return "Loading..."
      if (error) return `Something went wrong: ${error.message}`
      if (data)
        return (
          <div>
            <strong>Player data:</strong>
            <pre>{JSON.stringify(data, null, 2)}</pre>
          </div>
        )
      return null
    }}
  </Async>
)
```

> Using [helper components](/getting-started/usage#with-helper-components) can greatly improve readability of your render functions by not having to write all those conditional returns.

## As a factory

You can also create your own component instances, allowing you to preconfigure them with options such as default `onResolve` and `onReject` callbacks.

```jsx
import { createInstance } from "react-async"

const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

// createInstance takes a defaultOptions object and a displayName (both optional)
const AsyncPlayer = createInstance({ promiseFn: loadPlayer }, "AsyncPlayer")

const MyComponent = () => (
  <AsyncPlayer playerId={1}>
    <AsyncPlayer.Fulfilled>{player => `Hello ${player.name}`}</AsyncPlayer.Fulfilled>
  </AsyncPlayer>
)
```

## With helper components

Several [helper components](/getting-started/usage#helper-components) are available to improve legibility. They can be used with `useAsync` by passing in the state, or with `<Async>` by using Context. Each of these components simply enables or disables rendering of its children based on the current state.

```jsx
import { useAsync, IfPending, IfFulfilled, IfRejected } from "react-async"

const loadPlayer = async ({ playerId }, { signal }) => {
  // ...
}

const MyComponent = () => {
  const state = useAsync({ promiseFn: loadPlayer, playerId: 1 })
  return (
    <>
      <IfPending state={state}>Loading...</IfPending>
      <IfRejected state={state}>{error => `Something went wrong: ${error.message}`}</IfRejected>
      <IfFulfilled state={state}>
        {data => (
          <div>
            <strong>Player data:</strong>
            <pre>{JSON.stringify(data, null, 2)}</pre>
          </div>
        )}
      </IfFulfilled>
    </>
  )
}
```

### As compounds to `<Async>`

Each of the helper components are also available as static properties of `<Async>`. In this case you won't have to pass the state object, instead it will be automatically provided through Context.

```jsx
import Async from "react-async"

const loadPlayer = async ({ playerId }, { signal }) => {
  const res = await fetch(`/api/players/${playerId}`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}

const MyComponent = () => (
  <Async promiseFn={loadPlayer} playerId={1}>
    <Async.Pending>Loading...</Async.Pending>
    <Async.Fulfilled>
      {data => (
        <div>
          <strong>Player data:</strong>
          <pre>{JSON.stringify(data, null, 2)}</pre>
        </div>
      )}
    </Async.Fulfilled>
    <Async.Rejected>{error => `Something went wrong: ${error.message}`}</Async.Rejected>
  </Async>
)
```


# DevTools

React Async comes with a separate DevTools package which helps you Debug and develop your asynchronous application states. You can install it from npm:

```
npm install --save react-async-devtools
```

Or if you're using Yarn:

```
yarn add react-async-devtools
```

Then simply import it and render the`<DevTools />` component at the root of your app:

```jsx
import DevTools from "react-async-devtools"

export const Root = () => (
  <>
    <DevTools />
    <App />
  </>
)
```


# Interfaces

React Async provides several ways to use it. The classic interface is through the `<Async>` component, which is backwards compatible to React v16.3. More recent React applications will be using hooks, of which two are provided: `useAsync` and `useFetch`. Functionally, `<Async>` and `useAsync` are equivalent. `useFetch` is a special version of `useAsync` which is tied to the native `fetch` API.

React Async accepts a wide range of [configuration options](/api/options) and returns a set of [state props](/api/state). The way you use these differs slightly between the `useAsync` and `useFetch` hooks, and the `<Async>` component.

## `Async` component

```jsx
<Async {...options}>{state => ...}</Async>
```

* [`options`](/api/options) Configuration options
* [`state`](/api/state) State object

> We recommend that you pass the options individually, rather than using JSX [spread attributes](https://reactjs.org/docs/jsx-in-depth.html#spread-attributes). React Async uses [render props](https://reactjs.org/docs/render-props.html) to return its state back to you, so it can be used by other components further down the tree.

## `useAsync` hook

```javascript
const state = useAsync(options)
```

* [`state`](/api/state) State object
* [`options`](/api/options) Configuration options

> We recommend that you pass `options` as an inline object literal, and that you [destructure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) the `state` object to extract the properties you need, unless you have multiple instances in the same component.

## `useFetch` hook

```javascript
const state = useFetch(resource, init, options)
```

* [`state`](/api/state) State object
* [`resource`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax) The resource you want to fetch
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax) Custom request options
* [`options`](/api/options) Configuration options

## `createInstance`

Besides using the `Async` component directly, you can also create your own instance of it. This allows you to preload it with options, e.g. to enable global error handling.

```javascript
const CustomAsync = createInstance(defaultOptions, displayName)
```

* [`defaultOptions`](/api/options) Default configuration options
* `displayName` Name for this instance, used by React DevTools


# Configuration options

These can be passed in an object to `useAsync(options)`, or as props to `<Async {...options}>` and custom instances.

* [`promise`](/api/options#promise) An already started Promise instance.
* [`promiseFn`](/api/options#promisefn) Function that returns a Promise, automatically invoked.
* [`deferFn`](/api/options#deferfn) Function that returns a Promise, manually invoked with `run`.
* [`watch`](/api/options#watch) Watch a value and automatically reload when it changes.
* [`watchFn`](/api/options#watchfn) Watch this function and automatically reload when it returns truthy.
* [`initialValue`](/api/options#initialvalue) Provide initial data or error for server-side rendering.
* [`onResolve`](/api/options#onresolve) Callback invoked when Promise resolves.
* [`onReject`](/api/options#onreject) Callback invoked when Promise rejects.
* [`onCancel`](/api/options#oncancel) Callback invoked when a Promise is cancelled.
* [`reducer`](/api/options#reducer) State reducer to control internal state updates.
* [`dispatcher`](/api/options#dispatcher) Action dispatcher to control internal action dispatching.
* [`debugLabel`](/api/options#debuglabel) Unique label used in DevTools.
* [`suspense`](/api/options#suspense) Enable **experimental** Suspense integration.

`useFetch` additionally takes these options:

* [`defer`](/api/options#defer) Force the use of `deferFn` or `promiseFn`.
* [`json`](/api/options#json) Enable JSON parsing of the response.

## `promise`

> `Promise`

A Promise instance which has already started. It will simply add the necessary resolve/reject callbacks and set `startedAt` to the time `promise` was first provided. Changing the value of `promise` will cancel any pending promise and listen to the new one. If `promise` is initially undefined, the React Async state will be `pending`.

> Note that `reload` will not do anything when using `promise`. Use `promiseFn` instead.

## `promiseFn`

> `function(props: Object, controller: AbortController): Promise`

A function that returns a promise. It is automatically invoked in `componentDidMount` and `componentDidUpdate`. The function receives all component props (or options) and an AbortController instance as arguments.

> Be aware that updating `promiseFn` will trigger it to cancel any pending promise and load the new promise. Passing an inline (arrow) function will cause it to change and reload on every render of the parent component. You can avoid this by defining the `promiseFn` value **outside** of the render method. If you need to pass variables to the `promiseFn`, pass them as additional props to `<Async>`, as `promiseFn` will be invoked with these props. Alternatively you can use `useCallback` or [memoize-one](https://github.com/alexreardon/memoize-one) to avoid unnecessary updates.

## `deferFn`

> `function(args: any[], props: Object, controller: AbortController): Promise`

A function that returns a promise. This is invoked only by manually calling `run(...args)`. Any arguments to `run` are passed-through as an array via `args`, so you can pass data through either `args` or `props`, as needed. The `deferFn` is commonly used to send data to the server following a user action, such as submitting a form. You can use this in conjunction with `promiseFn` to fill the form with existing data, then updating it on submit with `deferFn`.

> Be aware that when using both `promiseFn` and `deferFn`, the shape of their fulfilled value should match, because they both update the same `data`.

## `watch`

> `any`

Watches this property through `componentDidUpdate` and re-runs the `promiseFn` when the value changes, using a simple reference check (`oldValue !== newValue`). If you need a more complex update check, use `watchFn` instead.

## `watchFn`

> `function(props: Object, prevProps: Object): boolean | any`

Re-runs the `promiseFn` when this callback returns truthy (called on every update). Any default props specified by `createInstance` are available too.

## `initialValue`

> `any | Error`

Initial state for `data` or `error` (if instance of Error); useful for server-side rendering. When an `initialValue` is provided, the `promiseFn` will not be invoked on first render. Instead, `status` will be immediately set to `fulfilled` or `rejected` and your components will render accordingly. If you want to trigger the `promiseFn` regardless, you can call `reload()` or use the `watch` or `watchFn` option.

> Note that `onResolve` or `onReject` is not invoked in this case and no `promise` prop will be created.

## `onResolve`

> `function(data: any): void`

Callback function invoked when a promise resolves, receives data as argument.

## `onReject`

> `function(reason: Error): void`

Callback function invoked when a promise rejects, receives rejection reason (error) as argument.

## `onCancel`

> `function(): void`

Callback function invoked when a promise is cancelled, either manually using `cancel()` or automatically due to props changes or unmounting.

## `reducer`

> `function(state: any, action: Object, internalReducer: function(state: any, action: Object))`

State reducer to take full control over state updates by wrapping the [internal reducer](https://github.com/async-library/react-async/blob/master/packages/react-async/src/reducer.ts). It receives the current state, the dispatched action and the internal reducer. You probably want to invoke the internal reducer at some point.

> This is a power feature which loosely follows the [state reducer pattern](https://kentcdodds.com/blog/the-state-reducer-pattern). It allows you to control state changes by intercepting actions before they are handled, or by overriding or enhancing the reducer itself.

## `dispatcher`

> `function(action: Object, internalDispatch: function(action: Object), props: Object)`

Action dispatcher to take full control over action dispatching by wrapping the internal dispatcher. It receives the original action, the internal dispatcher and all component props (or options). You probably want to invoke the internal dispatcher at some point.

> This is a power feature similar to the [state reducer pattern](https://kentcdodds.com/blog/the-state-reducer-pattern). It allows you to control state changes by intercepting actions before they are dispatched, to dispatch additional actions, possibly later in time.

## `debugLabel`

> `string`

A unique label to describe this React Async instance, used in React DevTools (through `useDebugValue`) and React Async DevTools.

## `suspense`

> `boolean`

Enables **experimental** Suspense integration. This will make React Async throw a promise while loading, so you can use Suspense to render a fallback UI, instead of using `<IfPending>`. Suspense differs in 2 main ways:

* `<Suspense>` should be an ancestor of your Async component, instead of a descendant. It can be anywhere up in the

  component hierarchy.
* You can have a single `<Suspense>` wrap multiple Async components, in which case it will render the fallback UI until

  all promises are settled.

> Note that the way Suspense is integrated right now may change. Until Suspense for data fetching is officially released, we may make breaking changes to its integration in React Async in a minor or patch release. Among other things, we'll probably add a cache of sorts.

## `defer`

> `boolean`

Enables the use of `deferFn` if `true`, or enables the use of `promiseFn` if `false`. By default this is automatically chosen based on the request method (`deferFn` for POST / PUT / PATCH / DELETE, `promiseFn` otherwise).

## `json`

> `boolean`

Enables or disables JSON parsing of the response body. By default this is automatically enabled if the `Accept` header is set to `"application/json"`.


# State properties

These are returned in an object by `useAsync()` or provided by `<Async>` as render props to the `children` function:

* [`data`](/api/state#data) Last resolved promise value, maintained when new error arrives.
* [`error`](/api/state#error) Rejected promise reason, cleared when new data arrives.
* [`value`](/api/state#value) The value of `data` or `error`, whichever was last updated.
* [`initialValue`](/api/state#initialvalue) The data or error that was provided through the `initialValue` prop.
* [`startedAt`](/api/state#startedat) When the current/last promise was started.
* [`finishedAt`](/api/state#finishedat) When the last promise was fulfilled or rejected.
* [`status`](/api/state#status) One of: `initial`, `pending`, `fulfilled`, `rejected`.
* [`isInitial`](/api/state#isinitial) true when no promise has ever started, or one started but was cancelled.
* [`isPending`](/api/state#ispending) true when a promise is currently awaiting settlement. Alias: `isLoading`
* [`isFulfilled`](/api/state#isfulfilled) true when the last promise was fulfilled. Alias: `isResolved`
* [`isRejected`](/api/state#isrejected) true when the last promise was rejected.
* [`isSettled`](/api/state#issettled) true when the last promise was fulfilled or rejected (not initial or pending).
* [`counter`](/api/state#counter) The number of times a promise was started.
* [`promise`](/api/state#promise) A reference to the internal wrapper promise, which can be chained on.
* [`run`](/api/state#run) Invokes the `deferFn`.
* [`reload`](/api/state#reload) Re-runs the promise when invoked, using any previous arguments.
* [`cancel`](/api/state#cancel) Cancel any pending promise.
* [`setData`](/api/state#setdata) Sets `data` to the passed value, unsets `error` and cancels any pending promise.
* [`setError`](/api/state#seterror) Sets `error` to the passed value and cancels any pending promise.

## `data`

> `any`

Last resolved promise value, maintained when new error arrives.

## `error`

> `Error`

Rejected promise reason, cleared when new data arrives.

## `value`

> `any | Error`

The data or error that was last provided (either through `initialValue` or by settling a promise).

## `initialValue`

> `any | Error`

The data or error that was originally provided through the `initialValue` prop.

## `startedAt`

> `Date`

Tracks when the current/last promise was started.

## `finishedAt`

> `Date`

Tracks when the last promise was resolved or rejected.

## `status`

> `string`

One of: `initial`, `pending`, `fulfilled`, `rejected`. These are available for import as `statusTypes`.

## `isInitial`

> `boolean`

`true` while no promise has started yet, or one was started but cancelled.

## `isPending`

> `boolean`

`true` while a promise is pending (loading), `false` otherwise.

Alias: `isLoading`

## `isFulfilled`

> `boolean`

`true` when the last promise was fulfilled (resolved to a value).

Alias: `isResolved`

## `isRejected`

> `boolean`

`true` when the last promise was rejected.

## `isSettled`

> `boolean`

`true` when the last promise was either fulfilled or rejected (i.e. not initial or pending)

## `counter`

> `number`

The number of times a promise was started.

## `promise`

> `Promise`

A reference to the internal wrapper promise created when starting a new promise (either automatically or by invoking `run` / `reload`). It fulfills or rejects along with the provided `promise` / `promiseFn` / `deferFn`. Useful as a chainable alternative to the `onResolve` / `onReject` callbacks.

Warning! If you chain on `promise`, you MUST provide a rejection handler (e.g. `.catch(...)`). Otherwise React will throw an exception and crash if the promise rejects.

## `run`

> `function(...args: any[]): void`

Runs the `deferFn`, passing any arguments provided as an array.

When used with `useFetch`, `run` has several overloaded signatures:

> `function(override: OverrideParams | (params: OverrideParams) => OverrideParams): void`
>
> `function(event: SyntheticEvent | Event): void`
>
> `function(): void`

Where `type OverrideParams = { resource?: RequestInfo } & Partial<RequestInit>`.

This way you can run the `fetch` request with custom `resource` and `init`. If `override` is an object it will be spread over the default `resource` and `init` for `fetch`. If it's a function it will be invoked with the params defined with `useFetch`, and should return an `override` object. This way you can either extend or override the value of `resource` and `init`, for example to change the URL or set custom request headers.

## `reload`

> `function(): void`

Re-runs the promise when invoked, using the previous arguments.

## `cancel`

> `function(): void`

Cancels the currently pending promise by ignoring its result and calls `abort()` on the AbortController.

## `setData`

> `function(data: any, callback?: () => void): any`

Function that sets `data` to the passed value, unsets `error` and cancels any pending promise. Takes an optional callback which is invoked after the state update is completed. Returns the data to enable chaining.

## `setError`

> `function(error: Error, callback?: () => void): Error`

Function that sets `error` to the passed value and cancels any pending promise. Takes an optional callback which is invoked after the state update is completed. Returns the error to enable chaining.


# Helper components

React Async provides several helper components that make your JSX more declarative and less cluttered. They don't have to be direct children of `<Async>` and you can use the same component several times.

## `<IfInitial>` / `<Async.Initial>`

Renders only while the deferred promise is still waiting to be run, or you have not provided any promise.

### Props

* `children` `function(state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `persist` `boolean` Show until we have data, even while loading or when an error occurred. By default it hides as soon

  as the promise starts loading.

### Examples

```jsx
const state = useAsync(...)
return (
  <IfInitial state={state}>
    <p>This text is only rendered while `run` has not yet been invoked on `deferFn`.</p>
  </IfInitial>
)
```

```jsx
<Async deferFn={deferFn}>
  <Async.Initial>
    <p>This text is only rendered while `run` has not yet been invoked on `deferFn`.</p>
  </Async.Initial>
</Async>
```

```jsx
<Async.Initial persist>
  {({ error, isPending, run }) => (
    <div>
      <p>This text is only rendered while the promise has not fulfilled yet.</p>
      <button onClick={run} disabled={!isPending}>
        Run
      </button>
      {error && <p>{error.message}</p>}
    </div>
  )}
</Async.Initial>
```

## `<IfPending>` / `<Async.Pending>`

This component renders only while the promise is pending (loading / unsettled).

Alias: `<Async.Loading>`

### Props

* `children` `function(state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `initial` `boolean` Show only on initial load (when `data` is `undefined`).

### Examples

```jsx
const state = useAsync(...)
return (
  <IfPending state={state}>
    <p>This text is only rendered while performing the initial load.</p>
  </IfPending>
)
```

```jsx
<Async.Pending initial>
  <p>This text is only rendered while performing the initial load.</p>
</Async.Pending>
```

```jsx
<Async.Pending>{({ startedAt }) => `Loading since ${startedAt.toISOString()}`}</Async.Pending>
```

## `<IfFulfilled>` / `<Async.Fulfilled>`

This component renders only when the promise is fulfilled (resolved to a value, could be `undefined`).

Alias: `<Async.Resolved>`

### Props

* `children` `function(data: any, state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `persist` `boolean` Show old data while loading new data. By default it hides as soon as a new promise starts.

### Examples

```jsx
const state = useAsync(...)
return (
  <IfFulfilled state={state}>
    {data => <pre>{JSON.stringify(data)}</pre>}
  </IfFulfilled>
)
```

```jsx
<Async.Fulfilled persist>{data => <pre>{JSON.stringify(data)}</pre>}</Async.Fulfilled>
```

```jsx
<Async.Fulfilled>
  {(data, { finishedAt }) => `Last updated ${finishedAt.toISOString()}`}
</Async.Fulfilled>
```

## `<IfRejected>` / `<Async.Rejected>`

This component renders only when the promise is rejected.

### Props

* `children` `function(error: Error, state: Object): Node | Node` Render function or React Node.
* `state` `object` Async state object (return value of `useAsync()`).
* `persist` `boolean` Show old error while loading new data. By default it hides as soon as a new promise starts.


# Async components

The most common use case for React Async is data fetching. In single-page applications it's very common to dynamically load some data from a backend. React Async makes it incredibly easy to set this up, without having to worry about the details.

The mental model of React Async is component-first. Rather than loading data high up in your application and passing it down to a component for display, you perform the data loading at the component level. Such a component is called an async component. An async component can render its state in a meaningful way like any other component, or be logic-only. In that case it doesn't render any UI but instead passes its state down to its children. Such separation of concerns is good practice.

## Creating an async component with `useFetch`

The easiest way to create an async component for data fetching is through the [`useFetch` hook](/api/interfaces#usefetch-hook):

```jsx
import React from "react"
import { useFetch } from "react-async"

const Person = ({ id }) => {
  const { data, error } = useFetch(`https://swapi.co/api/people/${id}/`, {
    headers: { accept: "application/json" },
  })
  if (error) return error.message
  if (data) return `Hi, my name is ${data.name}!`
  return null
}

const App = () => {
  return <Person id={1} />
}
```

## More flexibility with `useAsync`

For most data fetching needs, `useFetch` is sufficient. However, sometimes you may want to take full control, for example if you want to combine multiple requests. In this case you can use the [`useAsync` hook](/api/interfaces#useasync-hook).

The core concept of `useAsync` (and React Async in general), is the [`promiseFn`](/api/options#promisefn): a function that returns a `Promise`. It's the fundamental concept for modelling asynchronous operations. It enables React Async to take control over scheduling, the Promise lifecycle and things like (re)starting an operation on user action or other changes. We've deliberately chosen the `Promise` as our primitive, because it's natively supported and has various utility methods like `Promise.all`. That's also why you'll find our terminology closely follows the Promise [states and fates](https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md).

The above example, written with `useAsync`, would look like this:

```jsx
import React from "react"
import { useAsync } from "react-async"

const fetchPerson = async ({ id }, { signal }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal })
  if (!response.ok) throw new Error(response.status)
  return response.json()
}

const Person = ({ id }) => {
  const { data, error } = useAsync({ promiseFn: fetchPerson, id })
  if (error) return error.message
  if (data) return `Hi, my name is ${data.name}!`
  return null
}

const App = () => {
  return <Person id={1} />
}
```

Notice the incoming parameters to `fetchPerson`. The `promiseFn` will be invoked with a `props` object and an `AbortController`. `props` are the options you passed to `useAsync`, which is why you can access the `id` property using [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring). The `AbortController` is created by React Async to enable [abortable fetch](https://developers.google.com/web/updates/2017/09/abortable-fetch), so the underlying request will be aborted when the promise is cancelled (e.g. when a new one starts or we leave the page). We have to pass its `AbortSignal` down to `fetch` in order to wire this up.


# Separating view and logic

It's generally good practice to separate view components from logic components. Async components should preferably be logic-only. That means they don't render anything by themselves. Instead you can use the [render props](https://reactjs.org/docs/render-props.html) pattern to pass down the async state:

```jsx
import React from "react"
import { useAsync } from "react-async"

const fetchPerson = async ({ id }, { signal }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal })
  if (!response.ok) throw new Error(response.statusText)
  return response.json()
}

const Person = ({ id }) => {
  const state = useAsync({ promiseFn: fetchPerson, id })
  return children(state)
}

const App = () => {
  return (
    <Person id={1}>
      {({ isPending, data, error }) => {
        if (isPending) return "Loading..."
        if (error) return <ErrorMessage {...error} />
        if (data) return <Greeting {...data} />
        return null
      }}
    </Person>
  )
}
```

> `ErrorMessage` and `Greeting` would be separate view components defined elsewhere.

## Cleaning up the JSX

You'll notice the render props pattern is very powerful, but can also lead to code that's hard to read and understand. To make your JSX more declarative and less cluttered, you can use the [`<Async>`](/api/interfaces#async-component) component and its [state helpers](/api/helpers). These take away the need for `if/else` statements and `return` keywords in your JSX.

```jsx
import React from "react"
import Async from "react-async"

const fetchPerson = async ({ id }, { signal }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal })
  if (!response.ok) throw new Error(response.statusText)
  return response.json()
}

const App = () => {
  return (
    <Async promiseFn={fetchPerson} id={1}>
      <Async.Pending>Loading...</Async.Pending>
      <Async.Rejected>{error => <ErrorMessage {...error} />}</Async.Rejected>
      <Async.Fulfilled>{data => <Greeting {...data} />}</Async.Fulfilled>
    </Async>
  )
}
```

You should know that these helper components do not have to be direct children of the `<Async>` component. Because they are automatically wired up using [Context](https://reactjs.org/docs/context.html), they can be placed anywhere down the component tree, so long as they are descendants. You can also use helpers of the same type, multiple times.

Stand-alone versions of `<Async.Pending>` and the like are also available. However, these must be wired up manually by passing the `state` prop and are therefore only really useful when combined with one of the async hooks.


# Async actions

Fetching data for display alone isn't sufficient for most applications. You'll often also want to submit data back to the server, or handle other types of asynchronous actions. To enable this, React Async has the concept of a [`deferFn`](/api/options#deferfn).

Like `promiseFn`, a `deferFn` is a function that returns a Promise. The difference is that `deferFn` will not be automatically invoked by React Async when rendering the component. Instead it will have to be triggered by calling the [`run`](/api/state#run) function provided by React Async.

```jsx
import React, { useState } from "react"
import { useAsync } from "react-async"

const subscribe = ([email], props, { signal }) =>
  fetch("/newsletter", { method: "POST", body: JSON.stringify({ email }), signal })

const NewsletterForm = () => {
  const { isPending, error, run } = useAsync({ deferFn: subscribe })
  const [email, setEmail] = useState("")

  const handleSubmit = event => {
    event.preventDefault()
    run(email)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={event => setEmail(event.target.value)} />
      <button type="submit" disabled={isPending}>
        Subscribe
      </button>
      {error && <p>{error.message}</p>}
    </form>
  )
}
```

As you can see, the `deferFn` is invoked with 3 arguments: `args`, `props` and the AbortController. `args` is an array representing the arguments that were passed to `run`. In this case we passed the `email`, so we can extract that from the `args` array at the first index using [array destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Array_destructuring) and pass it along to our `fetch` request.

## Sending data with `useFetch`

The above example can be simplified when we rely on [`useFetch`](/api/interfaces#usefetch-hook) instead of constructing the request manually.

```jsx
import React, { useState } from "react"
import { useFetch } from "react-async"

const NewsletterForm = () => {
  const { isPending, error, run } = useFetch("/newsletter", { method: "POST" })
  const [email, setEmail] = useState("")

  const handleSubmit = event => {
    event.preventDefault()
    run({ body: JSON.stringify({ email }) })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={event => setEmail(event.target.value)} />
      <button type="submit" disabled={isPending}>
        Subscribe
      </button>
      {error && <p>{error.message}</p>}
    </form>
  )
}
```

The [`run`](/api/state#run) function for `useFetch` is a little special because it allows you to override the request's resource and other params. This way you can pass in the body, add dynamic headers or override the URL.


# Optimistic updates

A powerful pattern to improve your app's perceived performance is optimistic updates. When building an async action, you might be able to predict the outcome of the operation. If so, you can implement optimistic updates by proactively setting the `data` to the predicted value, when starting the async action. Once the action completes, it will update `data` to the actual value, probably the same value as predicted.

The following example uses both `promiseFn` and `deferFn` along with [`setData`](/api/state#setdata) to implement optimistic updates.

```jsx
import Async from "react-async"

const getAttendance = () => fetch("/attendance").then(() => true, () => false)
const updateAttendance = ([attend]) =>
  fetch("/attendance", { method: attend ? "POST" : "DELETE" }).then(() => attend, () => !attend)

const AttendanceToggle = () => (
  <Async promiseFn={getAttendance} deferFn={updateAttendance}>
    {({ isPending, data: isAttending, run, setData }) => (
      <Toggle
        on={isAttending}
        onClick={() => {
          setData(!isAttending)
          run(!isAttending)
        }}
        disabled={isPending}
      />
    )}
  </Async>
)
```

Here we have a switch to toggle attentance for an event. Clicking the toggle will most likely succeed, so we can predict the value it will have after completion (because we're just flipping a boolean).

Notice that React Async accepts both a `promiseFn` and a `deferFn` at the same time. This allows you to combine data fetching with performing actions. A typical example of where this is useful is with forms, where you first want to populate the fields with current values from the database, and send the new values back when submitting the form. Do note that `promiseFn` and `deferFn` operate on the same `data`, so they should both resolve to a similar kind of value.


# Server-side rendering

There's a good chance you're using React with Server-side rendering (SSR), as many applications require this to be successful. If you happen to be using Next.js, it's really easy to integrate React Async. The crux is in setting a [`initialValue`](/api/options#initialvalue), which is fetched server-side for initial page loads and passed along through rehydration.

```jsx
import fetch from "isomorphic-unfetch"

const fetchPerson = async ({ id }) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`)
  if (!response.ok) throw new Error(response.status)
  return response.json()
}

const Person = ({ id, person }) => (
  <Async promiseFn={fetchPerson} initialValue={person} id={id}>
    <Async.Pending>Loading...</Async.Pending>
    <Async.Rejected>{error => <ErrorMessage {...error} />}</Async.Rejected>
    <Async.Fulfilled>{data => <Greeting {...data} />}</Async.Fulfilled>
  </Async>
)

Person.getInitialProps = async ({ req }) => {
  const id = req.params.id
  const person = await fetchPerson({ id })
  return { id, person }
}
```

If React Async is provided an `initialValue`, it will not invoke the `promiseFn` on mount. Instead it will use the `initialValue` to immediately set `data` or `error`, and render accordingly.


# Introduction

Thanks for your interest in improving React Async! Contributions of any kind are welcome. Please refer to this guide before opening an issue or pull request.

This repo relies on Yarn workspaces, so you should [install](https://yarnpkg.com/en/docs/install) and use `yarn@1.3.2` or higher as the package manager for this project.

## Development guide

Please have the ***latest*** stable versions of the following on your machine

* node
* yarn

### Initial setup

To start working on React Async, clone the repo and bootstrap the project:

```bash
git clone https://github.com/async-library/react-async.git
cd react-async
yarn && yarn bootstrap && yarn test
```

Note that all work is done against the `next` branch, we only merge to `master` when doing a release.

### Working with Storybook

We use Storybook as a development environment, particularly for the DevTools. Spin it up using:

```bash
yarn start:storybook
```

This should open up Storybook in a browser at <http://localhost:6006/> Run it side-by-side with `yarn test --watch` during development. See [Testing](/contributing/introduction#testing).

### Linting

Use `yarn lint` to verify your code style before committing. It's highly recommended to install the Prettier and ESLint plugins for your IDE. Travis CI will fail your build on lint errors. Configure VS Code with the following settings:

```
"eslint.autoFixOnSave": true,
"eslint.packageManager": "yarn",
"eslint.options": {
  "cache": true,
  "cacheLocation": ".cache/eslint",
  "extensions": [".js", ".jsx", ".mjs", ".json", ".ts", ".tsx"]
},
"eslint.validate": [
  "javascript",
  "javascriptreact",
  {"language": "typescript", "autoFix": true },
  {"language": "typescriptreact", "autoFix": true }
],
"eslint.alwaysShowStatus": true
```

This should enable auto-fix for all source files, and give linting warnings and errors within your editor.

### Testing

Use the following command to test all packages in watch mode. Refer to the [Jest CLI options](https://jestjs.io/docs/en/cli#options) for details.

```bash
yarn test:watch
```

In general, this is sufficient during development. Travis CI will apply a more rigorous set of tests.

#### Testing for compatibility

```bash
yarn test:compat
```

This runs all tests using various versions of `react` and `react-dom`, to check for compatibility with older/newer versions of React. This is what CircleCI and Travis run.

### Working with the examples

In the `examples` folder, you will find sample React applications that use React Async in various ways with various other libraries. Please add a new example when introducing a major new feature. Make sure to add it to `now.json` so it is automatically deployed when merged to `master`.

To run sample examples on your local environments

```bash
yarn build:examples
yarn test:examples
yarn start:examples
```

### Resolving issues

Sometimes your dependencies might end up in a weird state, causing random issues, especially when working with the examples. In this case it often helps to run `yarn clean -y && yarn bootstrap`. This will delete `node_modules` from all packages/examples and do a clean install.


# Setting up

## Prerequisites

In order to develop React Async on your local machine, you'll need `git`, `node` and `yarn`.

### Git

To clone the repository, commit your changes and push them upstream, you'll need to have `git` [installed](https://www.atlassian.com/git/tutorials/install-git).

### Node.js

As a JavaScript project, we rely heavily on Node.js. It's recommended to use a version manager such as [fnm](https://github.com/Schniz/fnm) for Mac / Linux or [nvm-windows](https://github.com/coreybutler/nvm-windows) for Windows to install the latest Node.js with.

### Yarn

This repo relies on Yarn workspaces, so you should [install](https://yarnpkg.com/en/docs/install) and use `yarn@1.3.2` or higher as the package manager for this project.

## Project setup

To start working on React Async, clone the repository and bootstrap the project by running the following commands one-by-one:

```bash
git clone https://github.com/async-library/react-async.git
cd react-async
yarn install
yarn bootstrap
yarn test
```

This should install all dependencies, build and link the react-async and react-async-devtools packages to the examples, and finally run the unit tests. In the end it should succeed with a message (numbers may change):

```
Test Suites: 6 passed, 6 total
Tests:       136 passed, 136 total
```

> Note that all work is done against the `next` branch, we only merge to `master` when doing a release.

## Editor setup

We recommend using [Visual Studio Code](https://code.visualstudio.com/) with the following extensions:

* [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
* [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
* [DeepScan](https://marketplace.visualstudio.com/items?itemName=DeepScan.vscode-deepscan)
* [Oceanic Plus](https://marketplace.visualstudio.com/items?itemName=marcoms.oceanic-plus)

Make sure to enable `editor.formatOnSave`, so Prettier will automatically apply the right code style. For the full immersive experience you can also install and use the [Overpass Mono](https://overpassfont.org/) font.


# Development

React Async is a library without visual parts. Only the DevTools have a user interface you can spin up in a browser. Therefore the development workflow for the core library might be different from what you're used to. Generally, we use a TDD approach:

* Write a unit test for the new feature or bug you want to fix. Sometimes you can just extend an existing test.
* Fix the test by implementing the feature or bugfix. Now all tests should pass.
* Optionally refactor the code for performance, readability and style. Probably this will come up during PR review.

We use the GitHub pull request workflow. In practice this means your workflow looks like this:

* Fork the repo (or pull the latest upstream) under your own account.
* Make your changes, commit and push them. We don't enforce any commit message format.
* Open a pull request on the main repository against the `next` branch. Make sure to follow the template.
* We'll review your PR and will probably ask for some changes.
* Once ready, we'll merge your PR.
* Your changes will be in the next release.

## Working with Storybook

We use Storybook as a development environment for the DevTools. Spin it up using:

```bash
yarn start:storybook
```

This should open up Storybook in a browser at <http://localhost:6006/> Run it side-by-side with `yarn test --watch` during development. See [Testing](/contributing/development#testing).

## Working with the examples

In the `examples` folder, you will find sample React applications that use React Async in various ways with various other libraries. Please add a new example when introducing a major new feature. Make sure to add it to `now.json` so it is automatically deployed when merged to `master`.

To run sample examples on your local environments

```bash
yarn build:examples
yarn test:examples
yarn start:examples
```

## Resolving issues

Sometimes your dependencies might end up in a weird state, causing random issues, especially when working with the examples. In this case it often helps to run `yarn clean -y && yarn bootstrap`. This will delete `node_modules` from all packages/examples and do a clean install.


# Testing

Use the following command to test all packages in watch mode. Refer to the [Jest CLI options](https://jestjs.io/docs/en/cli#options) for details.

```bash
yarn test:watch
```

In general, this is sufficient during development. CircleCI and Travis will eventually apply a more rigorous set of tests against your pull request, including the ones below.

## Testing the examples

Because React Async is only a piece in a bigger puzzle, testing for integration with other libraries is very important. You can run the tests for all examples against your local changes with the following command:

```bash
yarn test:examples
```

If you want to add integration tests for compatibility with another library, please add an example for it.

## Testing for compatibility

```bash
yarn test:compat
```

This runs all tests using various versions of `react` and `react-dom`, to check for compatibility with older/newer versions of React. This is what CircleCI and Travis run.

## Linting

Use `yarn lint` to verify your code style before committing. It's highly recommended to install the Prettier and ESLint plugins for your IDE. CircleCI and Travis will fail your build on lint errors.


# Releasing

All ongoing development is done on the `next` branch. When preparing for a release, we'll create a `release` branch which will eventually be merged into `master`. This way, what's on `master` is always what's published on `npm`.

Release management is currently a manual process, to be performed by core team members only. Here's the process:

1. Create a `release` branch, usually based on `next`.
2. Open a pull request for `release` -> `master`
3. Write the release notes in the PR description.
4. Decide on the version number, taking care to follow semver. Do a pre-release before doing the actual release.
5. Run `yarn bump` to increment the version number in all `package.json` files as well as `lerna.json`.
6. Commit the version change as "Release vX.X.X" (using the correct version number).
7. Tag the release commit with `git tag vX.X.X` (using the correct version number).
8. Push the release commit AND tag: `git push --follow-tags`
9. Publish each package (in `./packages`) to npm using the script below.
10. Create a new release on GitHub and copy the release notes there.

```
yarn build:packages
cd packages/react-async
npm publish pkg
cd ../react-async-devtools
npm publish pkg
```

Take care to publish the `pkg` directory!


