0
# Asynchronous Conditions
1
2
Promise-based conditional rendering with loading states, error handling, and automatic promise cancellation. Enables clean handling of async operations within conditional rendering.
3
4
## Capabilities
5
6
### Async If Component
7
8
When the If component receives a Promise as its condition, it automatically handles the Promise lifecycle with loading, success, and error states.
9
10
```typescript { .api }
11
/**
12
* When condition is a Promise:
13
* - Renders Fallback block while Promise is pending
14
* - Renders Then block when Promise resolves
15
* - Renders Else block when Promise rejects
16
* - Supports promise cancellation via keepAlive prop
17
*/
18
const If: FC<{
19
condition: Promise<any>;
20
keepAlive?: boolean;
21
children: ReactNode;
22
}>;
23
```
24
25
**Usage Examples:**
26
27
```typescript
28
import { If, Then, Else, Fallback } from "react-if";
29
30
// Basic async condition
31
<If condition={fetchUserData()}>
32
<Fallback>
33
<LoadingSpinner />
34
</Fallback>
35
<Then>
36
{(userData) => <UserProfile user={userData} />}
37
</Then>
38
<Else>
39
{(error) => <ErrorMessage error={error} />}
40
</Else>
41
</If>
42
43
// With keepAlive to prevent cancellation
44
<If condition={fetchCriticalData()} keepAlive={true}>
45
<Fallback>
46
<div className="loading">Loading critical data...</div>
47
</Fallback>
48
<Then>
49
{(data) => <CriticalDataView data={data} />}
50
</Then>
51
<Else>
52
{(error) => (
53
<div className="error">
54
Failed to load: {error.message}
55
<button onClick={retry}>Retry</button>
56
</div>
57
)}
58
</Else>
59
</If>
60
```
61
62
### Fallback Component
63
64
Loading state component that renders while a Promise condition is pending.
65
66
```typescript { .api }
67
/**
68
* Must contain only a single child, which it renders as-is.
69
* Should not be used outside of an <If /> block whose condition prop is a promise.
70
* Renders while the parent Promise condition is pending.
71
*/
72
const Fallback: FC<{
73
children?: ReactNode | (() => JSX.Element);
74
}>;
75
```
76
77
**Usage Examples:**
78
79
```typescript
80
// Simple loading indicator
81
<Fallback>
82
<div className="spinner">Loading...</div>
83
</Fallback>
84
85
// Function children for dynamic loading states
86
<Fallback>
87
{() => <AdvancedLoadingSpinner progress={loadingProgress} />}
88
</Fallback>
89
90
// Rich loading experience
91
<Fallback>
92
<div className="loading-container">
93
<LoadingSpinner />
94
<p>Fetching your data...</p>
95
<ProgressBar />
96
</div>
97
</Fallback>
98
```
99
100
### Promise Result Handling
101
102
Then and Else blocks receive the Promise result as their first parameter when used with async conditions.
103
104
```typescript { .api }
105
/**
106
* Function children for Then/Else blocks receive:
107
* @param returnValue - The resolved value (Then) or rejection error (Else)
108
* @param promiseHistory - Array of all promises passed to If
109
* @param cancellablePromise - The specific promise that triggered this render
110
*/
111
type AsyncChildrenFunction = (
112
returnValue: any,
113
promiseHistory: CancellablePromise[],
114
cancellablePromise: ExtendablePromise<any>
115
) => JSX.Element;
116
117
/**
118
* Enhanced Then component for async conditions - when used with Promise conditions,
119
* function children receive the resolved value as the first parameter
120
*/
121
interface AsyncThenProps {
122
children?: ReactNode | AsyncChildrenFunction;
123
}
124
125
/**
126
* Enhanced Else component for async conditions - when used with Promise conditions,
127
* function children receive the rejection error as the first parameter
128
*/
129
interface AsyncElseProps {
130
children?: ReactNode | AsyncChildrenFunction;
131
}
132
133
**Usage Examples:**
134
135
```typescript
136
// Access resolved data in Then block
137
<Then>
138
{(userData, history, promise) => (
139
<div>
140
<h1>Welcome, {userData.name}!</h1>
141
<p>Account created: {userData.createdAt}</p>
142
<small>Request ID: {promise.requestId}</small>
143
</div>
144
)}
145
</Then>
146
147
// Handle errors in Else block
148
<Else>
149
{(error, history, promise) => (
150
<div className="error">
151
<h3>Error loading data</h3>
152
<p>{error.message}</p>
153
<details>
154
<summary>Technical Details</summary>
155
<pre>{JSON.stringify({ error, promiseCount: history.length }, null, 2)}</pre>
156
</details>
157
<button onClick={() => retry(promise)}>Retry</button>
158
</div>
159
)}
160
</Else>
161
```
162
163
## Advanced Usage Patterns
164
165
### Promise Cancellation Control
166
167
```typescript
168
const DataComponent = ({ shouldKeepAlive, dataPromise }) => (
169
<If condition={dataPromise} keepAlive={shouldKeepAlive}>
170
<Fallback>
171
<div>
172
Loading data...
173
{!shouldKeepAlive && <small>Will cancel if component unmounts</small>}
174
</div>
175
</Fallback>
176
<Then>
177
{(data) => <DataDisplay data={data} />}
178
</Then>
179
<Else>
180
{(error) => <ErrorDisplay error={error} />}
181
</Else>
182
</If>
183
);
184
```
185
186
### Multiple Async Operations
187
188
```typescript
189
const MultiDataComponent = () => {
190
const [currentPromise, setCurrentPromise] = useState(null);
191
192
const loadUser = () => setCurrentPromise(fetchUser());
193
const loadPosts = () => setCurrentPromise(fetchPosts());
194
195
return (
196
<div>
197
<button onClick={loadUser}>Load User</button>
198
<button onClick={loadPosts}>Load Posts</button>
199
200
<If condition={currentPromise}>
201
<Fallback>
202
<LoadingIndicator />
203
</Fallback>
204
<Then>
205
{(data, history) => (
206
<div>
207
<DataDisplay data={data} />
208
<p>Total requests: {history.length}</p>
209
</div>
210
)}
211
</Then>
212
<Else>
213
{(error) => <ErrorMessage error={error} />}
214
</Else>
215
</If>
216
</div>
217
);
218
};
219
```
220
221
### Promise Chaining and History
222
223
```typescript
224
const ChainedDataLoader = ({ userId }) => {
225
const loadUserAndPosts = async () => {
226
const user = await fetchUser(userId);
227
const posts = await fetchUserPosts(user.id);
228
return { user, posts };
229
};
230
231
return (
232
<If condition={loadUserAndPosts()}>
233
<Fallback>
234
<div>Loading user and posts...</div>
235
</Fallback>
236
<Then>
237
{(data, history, currentPromise) => (
238
<div>
239
<UserProfile user={data.user} />
240
<PostsList posts={data.posts} />
241
<DebugInfo>
242
Promise history: {history.length} total requests
243
Current promise: {currentPromise.constructor.name}
244
</DebugInfo>
245
</div>
246
)}
247
</Then>
248
<Else>
249
{(error, history) => (
250
<div>
251
<ErrorDisplay error={error} />
252
<RetryButton
253
onRetry={() => window.location.reload()}
254
attempts={history.length}
255
/>
256
</div>
257
)}
258
</Else>
259
</If>
260
);
261
};
262
```
263
264
### Conditional Promise Execution
265
266
```typescript
267
const ConditionalDataLoader = ({ shouldLoad, params }) => {
268
// Only create promise when needed
269
const dataPromise = shouldLoad ? fetchData(params) : null;
270
271
return (
272
<div>
273
{dataPromise ? (
274
<If condition={dataPromise}>
275
<Fallback>Loading...</Fallback>
276
<Then>{(data) => <DataView data={data} />}</Then>
277
<Else>{(error) => <ErrorView error={error} />}</Else>
278
</If>
279
) : (
280
<div>Click load to fetch data</div>
281
)}
282
</div>
283
);
284
};
285
```
286
287
## Type Definitions
288
289
```typescript { .api }
290
interface ExtendablePromise<T> extends Promise<T> {
291
[index: string]: any;
292
}
293
294
interface CancellablePromise {
295
promise: ExtendablePromise<any>;
296
cancel: () => void;
297
}
298
299
interface AsyncSupportProps {
300
/**
301
* - False (default): promises are cancelled before each unmount
302
* - True: promises can be fulfilled even after a
303
* component unmount or a change to promise prop
304
*/
305
keepAlive?: boolean;
306
}
307
308
type ComponentWithConditionPropsAsyncSupport = {
309
condition: Promise<any>;
310
keepAlive?: boolean;
311
children: ReactNode;
312
};
313
314
type AsyncChildrenFunction = (
315
returnValue: any,
316
promiseHistory: CancellablePromise[],
317
cancellablePromise: ExtendablePromise<any>
318
) => JSX.Element;
319
```