Skip to content

Commit c4fe8eb

Browse files
authored
Update API to better match Promise spec and add the 'status' prop (closes #35) (#37)
+ **Breaking change**: `Async.Pending` was renamed to `Async.Waiting` + Added the `status` prop, which can be one of `initial`, `pending`, `fulfilled` or `rejected` + Added `isInitial`, `isPending`, `isFulfilled` (with alias `isResolved`), `isRejected` and `isSettled` boolean props. `isLoading` is now an alias for `isPending`. + Added separate TypeScript types for each status, to make various props non-optional. The `pending` and `fulfilled` statuses were chosen over `loading` and `resolved` because they better match the [terminology in the Promise specification](https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md#readme). The `Pending` helper component was renamed to `Initial` accordingly, causing a breaking change.
2 parents 3d450b4 + 4cdf0ab commit c4fe8eb

File tree

20 files changed

+424
-190
lines changed

20 files changed

+424
-190
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ node_js:
44
cache:
55
directories:
66
- node_modules
7-
script: npm run test:compat
7+
script: npm run lint && npm run test:compat
88
after_success:
99
- bash <(curl -s https://codecov.io/bash) -e TRAVIS_NODE_VERSION

README.md

+94-41
Original file line numberDiff line numberDiff line change
@@ -382,9 +382,14 @@ is set to `"application/json"`.
382382
- `data` Last resolved promise value, maintained when new error arrives.
383383
- `error` Rejected promise reason, cleared when new data arrives.
384384
- `initialValue` The data or error that was provided through the `initialValue` prop.
385-
- `isLoading` Whether or not a Promise is currently pending.
386385
- `startedAt` When the current/last promise was started.
387386
- `finishedAt` When the last promise was resolved or rejected.
387+
- `status` One of: `initial`, `pending`, `fulfilled`, `rejected`.
388+
- `isInitial` true when no promise has ever started, or one started but was cancelled.
389+
- `isPending` true when a promise is currently awaiting settlement. Alias: `isLoading`
390+
- `isFulfilled` true when the last promise was fulfilled with a value. Alias: `isResolved`
391+
- `isRejected` true when the last promise was rejected with a reason.
392+
- `isSettled` true when the last promise was fulfilled or rejected (not initial or pending).
388393
- `counter` The number of times a promise was started.
389394
- `cancel` Cancel any pending promise.
390395
- `run` Invokes the `deferFn`.
@@ -410,12 +415,6 @@ Rejected promise reason, cleared when new data arrives.
410415
411416
The data or error that was originally provided through the `initialValue` prop.
412417

413-
#### `isLoading`
414-
415-
> `boolean`
416-
417-
`true` while a promise is pending, `false` otherwise.
418-
419418
#### `startedAt`
420419

421420
> `Date`
@@ -428,6 +427,47 @@ Tracks when the current/last promise was started.
428427
429428
Tracks when the last promise was resolved or rejected.
430429

430+
#### `status`
431+
432+
> `string`
433+
434+
One of: `initial`, `pending`, `fulfilled`, `rejected`.
435+
These are available for import as `statusTypes`.
436+
437+
#### `isInitial`
438+
439+
> `boolean`
440+
441+
`true` while no promise has started yet, or one was started but cancelled.
442+
443+
#### `isPending`
444+
445+
> `boolean`
446+
447+
`true` while a promise is pending (loading), `false` otherwise.
448+
449+
Alias: `isLoading`
450+
451+
#### `isFulfilled`
452+
453+
> `boolean`
454+
455+
`true` when the last promise was fulfilled (resolved) with a value.
456+
457+
Alias: `isResolved`
458+
459+
#### `isRejected`
460+
461+
> `boolean`
462+
463+
`true` when the last promise was rejected with an error.
464+
465+
#### `isSettled`
466+
467+
> `boolean`
468+
469+
`true` when the last promise was either fulfilled or rejected (i.e. not initial or pending)
470+
431471
#### `counter`
432472

433473
> `number`
@@ -471,10 +511,45 @@ invoked after the state update is completed. Returns the error to enable chainin
471511
React Async provides several helper components that make your JSX more declarative and less cluttered.
472512
They don't have to be direct children of `<Async>` and you can use the same component several times.
473513

474-
### `<Async.Loading>`
514+
### `<Async.Initial>`
515+
516+
Renders only while the deferred promise is still waiting to be run, or you have not provided any promise.
517+
518+
#### Props
519+
520+
- `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.
521+
- `children` `function(state: object): Node | Node` Render function or React Node.
522+
523+
#### Examples
524+
525+
```js
526+
<Async deferFn={deferFn}>
527+
<Async.Initial>
528+
<p>This text is only rendered while `run` has not yet been invoked on `deferFn`.</p>
529+
</Async.Initial>
530+
</Async>
531+
```
532+
533+
```js
534+
<Async.Initial persist>
535+
{({ error, isLoading, run }) => (
536+
<div>
537+
<p>This text is only rendered while the promise has not resolved yet.</p>
538+
<button onClick={run} disabled={!isLoading}>
539+
Run
540+
</button>
541+
{error && <p>{error.message}</p>}
542+
</div>
543+
)}
544+
</Async.Initial>
545+
```
546+
547+
### `<Async.Pending>`
475548

476549
This component renders only while the promise is loading (unsettled).
477550

551+
Alias: `<Async.Loading>`
552+
478553
#### Props
479554

480555
- `initial` `boolean` Show only on initial load (when `data` is `undefined`).
@@ -483,19 +558,21 @@ This component renders only while the promise is loading (unsettled).
483558
#### Examples
484559

485560
```js
486-
<Async.Loading initial>
561+
<Async.Pending initial>
487562
<p>This text is only rendered while performing the initial load.</p>
488-
</Async.Loading>
563+
</Async.Pending>
489564
```
490565

491566
```js
492-
<Async.Loading>{({ startedAt }) => `Loading since ${startedAt.toISOString()}`}</Async.Loading>
567+
<Async.Pending>{({ startedAt }) => `Loading since ${startedAt.toISOString()}`}</Async.Pending>
493568
```
494569

495-
### `<Async.Resolved>`
570+
### `<Async.Fulfilled>`
496571

497572
This component renders only when the promise is fulfilled with data (`data !== undefined`).
498573

574+
Alias: `<Async.Resolved>`
575+
499576
#### Props
500577

501578
- `persist` `boolean` Show old data while loading new data. By default it hides as soon as a new promise starts.
@@ -504,11 +581,11 @@ This component renders only when the promise is fulfilled with data (`data !== u
504581
#### Examples
505582

506583
```js
507-
<Async.Resolved persist>{data => <pre>{JSON.stringify(data)}</pre>}</Async.Resolved>
584+
<Async.Fulfilled persist>{data => <pre>{JSON.stringify(data)}</pre>}</Async.Fulfilled>
508585
```
509586

510587
```js
511-
<Async.Resolved>{({ finishedAt }) => `Last updated ${startedAt.toISOString()}`}</Async.Resolved>
588+
<Async.Fulfilled>{({ finishedAt }) => `Last updated ${startedAt.toISOString()}`}</Async.Fulfilled>
512589
```
513590

514591
### `<Async.Rejected>`
@@ -530,39 +607,15 @@ This component renders only when the promise is rejected.
530607
<Async.Rejected>{error => `Unexpected error: ${error.message}`}</Async.Rejected>
531608
```
532609

533-
### `<Async.Pending>`
610+
### `<Async.Settled>`
534611

535-
Renders only while the deferred promise is still pending (not yet run), or you have not provided any promise.
612+
This component renders only when the promise is fulfilled or rejected.
536613

537614
#### Props
538615

539-
- `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.
616+
- `persist` `boolean` Show old data or error while loading new data. By default it hides as soon as a new promise starts.
540617
- `children` `function(state: object): Node | Node` Render function or React Node.
541618

542-
#### Examples
543-
544-
```js
545-
<Async deferFn={deferFn}>
546-
<Async.Pending>
547-
<p>This text is only rendered while `run` has not yet been invoked on `deferFn`.</p>
548-
</Async.Pending>
549-
</Async>
550-
```
551-
552-
```js
553-
<Async.Pending persist>
554-
{({ error, isLoading, run }) => (
555-
<div>
556-
<p>This text is only rendered while the promise has not resolved yet.</p>
557-
<button onClick={run} disabled={!isLoading}>
558-
Run
559-
</button>
560-
{error && <p>{error.message}</p>}
561-
</div>
562-
)}
563-
</Async.Pending>
564-
```
565-
566619
## Usage examples
567620

568621
Here's several examples to give you an idea of what's possible with React Async. For fully working examples, please

examples/basic-fetch/src/index.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,19 @@ const UserDetails = ({ data }) => (
2727
const App = () => (
2828
<>
2929
<Async promiseFn={loadUser} userId={1}>
30-
{({ data, error, isLoading }) => {
31-
if (isLoading) return <UserPlaceholder />
30+
{({ data, error, isPending }) => {
31+
if (isPending) return <UserPlaceholder />
3232
if (error) return <p>{error.message}</p>
3333
if (data) return <UserDetails data={data} />
3434
return null
3535
}}
3636
</Async>
3737

3838
<Async promiseFn={loadUser} userId={2}>
39-
<Async.Loading>
39+
<Async.Pending>
4040
<UserPlaceholder />
41-
</Async.Loading>
42-
<Async.Resolved>{data => <UserDetails data={data} />}</Async.Resolved>
41+
</Async.Pending>
42+
<Async.Fulfilled>{data => <UserDetails data={data} />}</Async.Fulfilled>
4343
<Async.Rejected>{error => <p>{error.message}</p>}</Async.Rejected>
4444
</Async>
4545
</>

examples/basic-hook/src/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ const UserDetails = ({ data }) => (
2626
)
2727

2828
const User = ({ userId }) => {
29-
const { data, error, isLoading } = useAsync({ promiseFn: loadUser, userId })
30-
if (isLoading) return <UserPlaceholder />
29+
const { data, error, isPending } = useAsync({ promiseFn: loadUser, userId })
30+
if (isPending) return <UserPlaceholder />
3131
if (error) return <p>{error.message}</p>
3232
if (data) return <UserDetails data={data} />
3333
return null

examples/custom-instance/src/index.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ const UserDetails = ({ data }) => (
2929
const App = () => (
3030
<>
3131
<AsyncUser userId={1}>
32-
{({ data, error, isLoading }) => {
33-
if (isLoading) return <UserPlaceholder />
32+
{({ data, error, isPending }) => {
33+
if (isPending) return <UserPlaceholder />
3434
if (error) return <p>{error.message}</p>
3535
if (data) return <UserDetails data={data} />
3636
return null
3737
}}
3838
</AsyncUser>
3939

4040
<AsyncUser userId={2}>
41-
<AsyncUser.Loading>
41+
<AsyncUser.Pending>
4242
<UserPlaceholder />
43-
</AsyncUser.Loading>
44-
<AsyncUser.Resolved>{data => <UserDetails data={data} />}</AsyncUser.Resolved>
43+
</AsyncUser.Pending>
44+
<AsyncUser.Fulfilled>{data => <UserDetails data={data} />}</AsyncUser.Fulfilled>
4545
<AsyncUser.Rejected>{error => <p>{error.message}</p>}</AsyncUser.Rejected>
4646
</AsyncUser>
4747
</>

examples/movie-app/src/App.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ const TopMovies = ({ handleSelect }) => (
6262
</span>
6363
</h1>
6464
<Async promiseFn={fetchMovies}>
65-
<Async.Loading>
65+
<Async.Pending>
6666
<p>Loading...</p>
67-
</Async.Loading>
68-
<Async.Resolved>
67+
</Async.Pending>
68+
<Async.Fulfilled>
6969
{movies =>
7070
movies.map(movie => <Movie {...movie} key={movie.id} onSelect={handleSelect(movie)} />)
7171
}
72-
</Async.Resolved>
72+
</Async.Fulfilled>
7373
</Async>
7474
</Fragment>
7575
)
@@ -99,10 +99,10 @@ const Details = ({ onBack, id }) => (
9999
</span>
100100
</button>
101101
<Async promiseFn={fetchMovieDetails} id={id} onResolve={console.log}>
102-
<Async.Loading>
102+
<Async.Pending>
103103
<p>Loading...</p>
104-
</Async.Loading>
105-
<Async.Resolved>
104+
</Async.Pending>
105+
<Async.Fulfilled>
106106
{movie => (
107107
<Fragment>
108108
<div className="main">
@@ -126,15 +126,15 @@ const Details = ({ onBack, id }) => (
126126
</div>
127127
<div className="reviews">
128128
<Async promiseFn={fetchMovieReviews} id={id} onResolve={console.log}>
129-
<Async.Loading>
129+
<Async.Pending>
130130
<p>Loading...</p>
131-
</Async.Loading>
132-
<Async.Resolved>{reviews => reviews.map(Review)}</Async.Resolved>
131+
</Async.Pending>
132+
<Async.Fulfilled>{reviews => reviews.map(Review)}</Async.Fulfilled>
133133
</Async>
134134
</div>
135135
</Fragment>
136136
)}
137-
</Async.Resolved>
137+
</Async.Fulfilled>
138138
</Async>
139139
</div>
140140
)

examples/with-abortcontroller/src/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ const download = (args, props, controller) =>
99
.then(res => res.json())
1010

1111
const App = () => {
12-
const { run, cancel, isLoading } = useAsync({ deferFn: download })
12+
const { run, cancel, isPending } = useAsync({ deferFn: download })
1313
return (
1414
<>
15-
{isLoading ? <button onClick={cancel}>cancel</button> : <button onClick={run}>start</button>}
16-
{isLoading ? (
15+
{isPending ? <button onClick={cancel}>cancel</button> : <button onClick={run}>start</button>}
16+
{isPending ? (
1717
<p>Loading...</p>
1818
) : (
1919
<p>Inspect network traffic to see requests being canceled.</p>

examples/with-nextjs/pages/index.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ class Hello extends React.Component {
2020
const { userId = 1 } = url.query
2121
return (
2222
<Async promiseFn={loadUser} userId={userId} watch={userId} initialValue={data}>
23-
<Async.Loading>
23+
<Async.Pending>
2424
<p>Loading...</p>
25-
</Async.Loading>
26-
<Async.Resolved>
25+
</Async.Pending>
26+
<Async.Fulfilled>
2727
{data => (
2828
<>
2929
<p>
@@ -43,7 +43,7 @@ class Hello extends React.Component {
4343
</p>
4444
</>
4545
)}
46-
</Async.Resolved>
46+
</Async.Fulfilled>
4747
<i>
4848
This data is initially loaded server-side, then client-side when navigating prev/next.
4949
</i>
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react"
22

3-
const Contributors = ({ data, error, isLoading }) => {
4-
if (isLoading) return "Loading Contributers..."
3+
const Contributors = ({ data, error, isPending }) => {
4+
if (isPending) return "Loading Contributers..."
55
if (error) return "Error"
66
return (
77
<ul>
@@ -12,4 +12,4 @@ const Contributors = ({ data, error, isLoading }) => {
1212
)
1313
}
1414

15-
export default Contributors
15+
export default Contributors

0 commit comments

Comments
 (0)