Skip to content

Commit 83b78af

Browse files
committed
Initial commit.
0 parents  commit 83b78af

File tree

7 files changed

+6991
-0
lines changed

7 files changed

+6991
-0
lines changed

.babelrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"presets": [["env", { "loose": true }], "stage-2", "react"],
3+
"ignore": ["node_modules", "stories.js"]
4+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules/

README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# react-async
2+
3+
React component for declarative promise resolution and data fetching. Uses render props for full flexibility or Context
4+
for convenience. Makes no assumptions about the shape of your data, the type of request or how you deal with loading
5+
states and errors. Supports optimistic updates.
6+
7+
## Install
8+
9+
`npm install --save react-async`
10+
11+
## Usage
12+
13+
Using render props for ultimate flexibility:
14+
15+
```js
16+
import Async from "react-async"
17+
18+
const loadJson = () => fetch("/some/url").then(res => res.json())
19+
20+
const MyComponent = () => {
21+
return (
22+
<Async promiseFn={loadJson}>
23+
{({ data, error, isLoading }) => {
24+
if (isLoading) return "Loading..."
25+
if (error) return `Something went wrong: ${error.message}`
26+
if (data)
27+
return (
28+
<div>
29+
<strong>Loaded some data:</strong>
30+
<pre>{JSON.stringify(data, null, 2)}</pre>
31+
</div>
32+
)
33+
return null
34+
}}
35+
</Async>
36+
)
37+
}
38+
```
39+
40+
Using helper components (don't have to be direct children) for ease of use:
41+
42+
```js
43+
import Async from "react-async"
44+
45+
const loadJson = () => fetch("/some/url").then(res => res.json())
46+
47+
const MyComponent = () => {
48+
return (
49+
<Async promiseFn={loadJson}>
50+
<Async.Loading>Loading...</Async.Loading>
51+
<Async.Resolved>
52+
{data => (
53+
<div>
54+
<strong>Loaded some data:</strong>
55+
<pre>{JSON.stringify(data, null, 2)}</pre>
56+
</div>
57+
)}
58+
</Async.Resolved>
59+
<Async.Rejected>{error => `Something went wrong: ${error.message}`}</Async.Rejected>
60+
</Async>
61+
)
62+
}
63+
```
64+
65+
`<Async>` is promise-based, so you can resolve anything you want, not just `fetch` requests.
66+
67+
### Props
68+
69+
`<Async>` takes the following properties:
70+
71+
- `promiseFn` [() => Promise] A function that returns a promise; invoked immediately in `componentDidMount` and without arguments
72+
- `deferFn` [() => Promise] A function that returns a promise; invoked only by calling `run`, with arguments being passed through
73+
- `watch` [any] Watches this property through `componentDidUpdate` and re-runs the `promiseFn` when the value changes (`oldValue !== newValue`)
74+
- `onResolve` [Function] Callback function invoked when a promise resolves, receives data as argument
75+
- `onReject` [Function] Callback function invoked when a promise rejects, receives error as argument
76+
77+
### Render props
78+
79+
`<Async>` provides the following render props:
80+
81+
- `data` [any] last resolved promise value, maintained when new error arrives
82+
- `error` [Error] rejected promise reason, cleared when new data arrives
83+
- `isLoading` [boolean] `true` while a promise is pending
84+
- `startedAt` [Date] when the current/last promise was started
85+
- `finishedAt` [Date] when the last promise was resolved or rejected
86+
- `cancel` [Function] ignores the result of the currently pending promise
87+
- `run` [Function] runs the `deferFn`, passing any arguments provided
88+
- `reload` [Function] re-runs the promise when invoked, using the previous arguments
89+
- `setData` [Function] sets `data` to the passed value, unsets `error` and cancels any pending promise
90+
- `setError` [Function] sets `error` to the passed value and cancels any pending promise
91+
92+
## Examples
93+
94+
### Basic data fetching with loading indicator, error state and retry
95+
96+
```js
97+
<Async promiseFn={loadJson}>
98+
{({ data, error, isLoading, reload }) => {
99+
if (isLoading) {
100+
return <div>Loading...</div>
101+
}
102+
if (error) {
103+
return (
104+
<div>
105+
<p>{error.toString()}</p>
106+
<button onClick={reload}>try again</button>
107+
</div>
108+
)
109+
}
110+
if (data) {
111+
return <pre>{JSON.stringify(data, null, 2)}</pre>
112+
}
113+
return null
114+
}}
115+
</Async>
116+
```
117+
118+
### Using `deferFn` to trigger an update (e.g. POST / PUT request)
119+
120+
```js
121+
<Async deferFn={subscribeToNewsletter}>
122+
{({ error, isLoading, run }) => (
123+
<form onSubmit={run}>
124+
<input type="email" name="email" />
125+
<button type="submit" disabled={isLoading}>
126+
Subscribe
127+
</button>
128+
{error && <p>{error.toString()}</p>}
129+
</form>
130+
)}
131+
</Async>
132+
```
133+
134+
### Using both `promiseFn` and `deferFn` along with `setData` to implement optimistic updates
135+
136+
```js
137+
const updateAttendance = attend => fetch(...).then(() => attend, () => !attend)
138+
139+
<Async promiseFn={getAttendance} deferFn={updateAttendance}>
140+
{({ data: isAttending, isLoading, run, setData }) => (
141+
<Toggle
142+
on={isAttending}
143+
onClick={() => {
144+
setData(!isAttending)
145+
run(!isAttending)
146+
}}
147+
disabled={isLoading}
148+
/>
149+
)}
150+
</Async>
151+
```

0 commit comments

Comments
 (0)