Skip to content

Commit 8fa6eac

Browse files
authored
Add support for the promise prop (closes #27) (#28)
* Add the `promise` prop. * Add tests for 'promise' prop. * Share common tests between component and hook.
1 parent b880a1c commit 8fa6eac

File tree

7 files changed

+490
-692
lines changed

7 files changed

+490
-692
lines changed

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ const MyComponent = () => (
278278

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

281+
- `promise` An already started Promise instance.
281282
- `promiseFn` Function that returns a Promise, automatically invoked.
282283
- `deferFn` Function that returns a Promise, manually invoked with `run`.
283284
- `watch` Watch a value and automatically reload when it changes.
@@ -286,6 +287,16 @@ These can be passed in an object to `useAsync()`, or as props to `<Async>` and c
286287
- `onResolve` Callback invoked when Promise resolves.
287288
- `onReject` Callback invoked when Promise rejects.
288289

290+
#### `promise`
291+
292+
> `Promise`
293+
294+
A Promise instance which has already started. It will simply add the necessary resolve/reject callbacks and set
295+
`startedAt` to the time `promise` was first provided. Changing the value of `promise` will cancel any pending promise
296+
and listen to the new one. If `promise` is initially undefined, the React Async state will be `pending`.
297+
298+
> Note that `reload` will not do anything when using `promise`. Use `promiseFn` instead.
299+
289300
#### `promiseFn`
290301

291302
> `function(props: object, controller: AbortController): Promise`
@@ -441,7 +452,7 @@ They don't have to be direct children of `<Async>` and you can use the same comp
441452

442453
### `<Async.Loading>`
443454

444-
This component renders only while the promise is loading.
455+
This component renders only while the promise is loading (unsettled).
445456

446457
#### Props
447458

@@ -462,7 +473,7 @@ This component renders only while the promise is loading.
462473

463474
### `<Async.Resolved>`
464475

465-
This component renders only when the promise is resolved with data (`data !== undefined`).
476+
This component renders only when the promise is fulfilled with data (`data !== undefined`).
466477

467478
#### Props
468479

@@ -500,7 +511,7 @@ This component renders only when the promise is rejected.
500511

501512
### `<Async.Pending>`
502513

503-
Renders only while the deferred promise is still pending (not yet run).
514+
Renders only while the deferred promise is still pending (not yet run), or you have not provided any promise.
504515

505516
#### Props
506517

src/Async.js

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
2727
this.setData = this.setData.bind(this)
2828
this.setError = this.setError.bind(this)
2929

30+
const promise = props.promise
3031
const promiseFn = props.promiseFn || defaultProps.promiseFn
3132
const initialValue = props.initialValue || defaultProps.initialValue
3233
const initialError = initialValue instanceof Error ? initialValue : undefined
@@ -40,8 +41,8 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
4041
initialValue,
4142
data: initialData,
4243
error: initialError,
43-
isLoading: !initialValue && isFunction(promiseFn),
44-
startedAt: undefined,
44+
isLoading: !!promise || (promiseFn && !initialValue),
45+
startedAt: promise ? new Date() : undefined,
4546
finishedAt: initialValue ? new Date() : undefined,
4647
counter: this.counter,
4748
cancel: this.cancel,
@@ -57,14 +58,20 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
5758

5859
componentDidMount() {
5960
this.mounted = true
60-
this.state.initialValue || this.load()
61+
if (this.props.promise || !this.state.initialValue) {
62+
this.load()
63+
}
6164
}
6265

6366
componentDidUpdate(prevProps) {
64-
const { watch, watchFn = defaultProps.watchFn, promiseFn } = this.props
67+
const { watch, watchFn = defaultProps.watchFn, promise, promiseFn } = this.props
6568
if (watch !== prevProps.watch) this.load()
6669
if (watchFn && watchFn({ ...defaultProps, ...this.props }, { ...defaultProps, ...prevProps }))
6770
this.load()
71+
if (promise !== prevProps.promise) {
72+
if (promise) this.load()
73+
else this.cancel()
74+
}
6875
if (promiseFn !== prevProps.promiseFn) {
6976
if (promiseFn) this.load()
7077
else this.cancel()
@@ -91,24 +98,32 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
9198
}
9299

93100
load() {
101+
const promise = this.props.promise
102+
if (promise) {
103+
this.start()
104+
return promise.then(this.onResolve(this.counter), this.onReject(this.counter))
105+
}
106+
94107
const promiseFn = this.props.promiseFn || defaultProps.promiseFn
95-
if (!promiseFn) return
96-
this.start()
97-
return promiseFn(this.props, this.abortController).then(
98-
this.onResolve(this.counter),
99-
this.onReject(this.counter)
100-
)
108+
if (promiseFn) {
109+
this.start()
110+
return promiseFn(this.props, this.abortController).then(
111+
this.onResolve(this.counter),
112+
this.onReject(this.counter)
113+
)
114+
}
101115
}
102116

103117
run(...args) {
104118
const deferFn = this.props.deferFn || defaultProps.deferFn
105-
if (!deferFn) return
106-
this.args = args
107-
this.start()
108-
return deferFn(args, { ...defaultProps, ...this.props }, this.abortController).then(
109-
this.onResolve(this.counter),
110-
this.onReject(this.counter)
111-
)
119+
if (deferFn) {
120+
this.args = args
121+
this.start()
122+
return deferFn(args, { ...defaultProps, ...this.props }, this.abortController).then(
123+
this.onResolve(this.counter),
124+
this.onReject(this.counter)
125+
)
126+
}
112127
}
113128

114129
cancel() {
@@ -162,6 +177,7 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
162177
if (PropTypes) {
163178
Async.propTypes = {
164179
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
180+
promise: PropTypes.instanceOf(Promise),
165181
promiseFn: PropTypes.func,
166182
deferFn: PropTypes.func,
167183
watch: PropTypes.any,

0 commit comments

Comments
 (0)