Skip to content

Commit beccf07

Browse files
committed
Add 'value' prop and update helper components to use the new status props.
1 parent 94ec322 commit beccf07

File tree

5 files changed

+43
-29
lines changed

5 files changed

+43
-29
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ is set to `"application/json"`.
391391

392392
- `data` Last resolved promise value, maintained when new error arrives.
393393
- `error` Rejected promise reason, cleared when new data arrives.
394+
- `value` The value of `data` or `error`, whichever was last updated.
394395
- `initialValue` The data or error that was provided through the `initialValue` prop.
395396
- `startedAt` When the current/last promise was started.
396397
- `finishedAt` When the last promise was fulfilled or rejected.
@@ -419,6 +420,12 @@ Last resolved promise value, maintained when new error arrives.
419420
420421
Rejected promise reason, cleared when new data arrives.
421422

423+
#### `value`
424+
425+
> `any | Error`
426+
427+
The data or error that was last provided (either through `initialValue` or by settling a promise).
428+
422429
#### `initialValue`
423430

424431
> `any | Error`

src/Async.js

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ try {
77
} catch (e) {}
88

99
const isFunction = arg => typeof arg === "function"
10+
const renderFn = (children, ...args) =>
11+
isFunction(children) ? children(...args) : children === undefined ? null : children
1012

1113
/**
1214
* createInstance allows you to create instances of Async that are bound to a specific promise.
@@ -186,12 +188,7 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
186188
*/
187189
const Initial = ({ children, persist }) => (
188190
<Consumer>
189-
{state => {
190-
if (state.data !== undefined) return null
191-
if (!persist && state.isPending) return null
192-
if (!persist && state.error !== undefined) return null
193-
return isFunction(children) ? children(state) : children || null
194-
}}
191+
{state => (state.isInitial || (persist && !state.data) ? renderFn(children, state) : null)}
195192
</Consumer>
196193
)
197194

@@ -206,15 +203,11 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
206203
* Renders only while pending (promise is loading).
207204
*
208205
* @prop {Function|Node} children Function (passing state) or React node
209-
* @prop {boolean} initial Show only on initial load (data is undefined)
206+
* @prop {boolean} initial Show only on initial load (data/error is undefined)
210207
*/
211208
const Pending = ({ children, initial }) => (
212209
<Consumer>
213-
{state => {
214-
if (!state.isPending) return null
215-
if (initial && state.data !== undefined) return null
216-
return isFunction(children) ? children(state) : children || null
217-
}}
210+
{state => (state.isPending && (!initial || !state.value) ? renderFn(children, state) : null)}
218211
</Consumer>
219212
)
220213

@@ -233,12 +226,9 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
233226
*/
234227
const Fulfilled = ({ children, persist }) => (
235228
<Consumer>
236-
{state => {
237-
if (state.data === undefined) return null
238-
if (!persist && state.isPending) return null
239-
if (!persist && state.error !== undefined) return null
240-
return isFunction(children) ? children(state.data, state) : children || null
241-
}}
229+
{state =>
230+
state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null
231+
}
242232
</Consumer>
243233
)
244234

@@ -257,11 +247,9 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
257247
*/
258248
const Rejected = ({ children, persist }) => (
259249
<Consumer>
260-
{state => {
261-
if (state.error === undefined) return null
262-
if (state.isPending && !persist) return null
263-
return isFunction(children) ? children(state.error, state) : children || null
264-
}}
250+
{state =>
251+
state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null
252+
}
265253
</Consumer>
266254
)
267255

@@ -276,15 +264,11 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => {
276264
* Renders only when promise is fulfilled or rejected.
277265
*
278266
* @prop {Function|Node} children Function (passing state) or React node
279-
* @prop {boolean} persist Show old data or error while pending (promise is loading)
267+
* @prop {boolean} persist Continue rendering while loading new data
280268
*/
281269
const Settled = ({ children, persist }) => (
282270
<Consumer>
283-
{state => {
284-
if (state.isInitial) return null
285-
if (state.isPending && !persist) return null
286-
return isFunction(children) ? children(state) : children || null
287-
}}
271+
{state => (state.isSettled || (persist && state.value) ? renderFn(children, state) : null)}
288272
</Consumer>
289273
)
290274

src/Async.spec.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,22 @@ describe("Async.Settled", () => {
189189
await waitForElement(() => getByText("err"))
190190
expect(queryByText("err")).toBeInTheDocument()
191191
})
192+
193+
test("renders while loading new data and persist=true", async () => {
194+
const promiseFn = () => resolveTo("value")
195+
const { getByText, queryByText } = render(
196+
<Async initialValue="init" promiseFn={promiseFn}>
197+
<Async.Pending>
198+
<Async.Settled persist>loading</Async.Settled>
199+
</Async.Pending>
200+
<Async.Settled>{({ reload }) => <button onClick={reload}>reload</button>}</Async.Settled>
201+
</Async>
202+
)
203+
expect(queryByText("loading")).toBeNull()
204+
fireEvent.click(getByText("reload"))
205+
await waitForElement(() => getByText("loading"))
206+
expect(queryByText("loading")).toBeInTheDocument()
207+
})
192208
})
193209

194210
describe("createInstance", () => {

src/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export type AsyncInitial<T> = AbstractState<T> & {
3434
initialValue?: undefined
3535
data: undefined
3636
error: undefined
37+
value: undefined
3738
startedAt: undefined
3839
finishedAt: undefined
3940
status: "initial"
@@ -48,6 +49,7 @@ export type AsyncInitial<T> = AbstractState<T> & {
4849
export type AsyncPending<T> = AbstractState<T> & {
4950
data: T | undefined
5051
error: Error | undefined
52+
value: T | Error | undefined
5153
startedAt: Date
5254
finishedAt: undefined
5355
status: "pending"
@@ -62,6 +64,7 @@ export type AsyncPending<T> = AbstractState<T> & {
6264
export type AsyncFulfilled<T> = AbstractState<T> & {
6365
data: T
6466
error: undefined
67+
value: T
6568
startedAt: Date
6669
finishedAt: Date
6770
status: "fulfilled"
@@ -76,6 +79,7 @@ export type AsyncFulfilled<T> = AbstractState<T> & {
7679
export type AsyncRejected<T> = AbstractState<T> & {
7780
data: T | undefined
7881
error: Error
82+
value: Error
7983
startedAt: Date
8084
finishedAt: Date
8185
status: "rejected"

src/reducer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const init = ({ initialValue, promise, promiseFn }) => ({
1111
initialValue,
1212
data: initialValue instanceof Error ? undefined : initialValue,
1313
error: initialValue instanceof Error ? initialValue : undefined,
14+
value: initialValue,
1415
startedAt: promise || promiseFn ? new Date() : undefined,
1516
finishedAt: initialValue ? new Date() : undefined,
1617
...getStatusProps(getInitialStatus(initialValue, promise || promiseFn)),
@@ -39,6 +40,7 @@ export const reducer = (state, { type, payload, meta }) => {
3940
return {
4041
...state,
4142
data: payload,
43+
value: payload,
4244
error: undefined,
4345
finishedAt: new Date(),
4446
...getStatusProps(statusTypes.fulfilled),
@@ -47,6 +49,7 @@ export const reducer = (state, { type, payload, meta }) => {
4749
return {
4850
...state,
4951
error: payload,
52+
value: payload,
5053
finishedAt: new Date(),
5154
...getStatusProps(statusTypes.rejected),
5255
}

0 commit comments

Comments
 (0)