0
# Helper Effects
1
2
High-level helper effects built on top of basic effects for common patterns like handling every action, latest action, throttling, and debouncing.
3
4
## Capabilities
5
6
### takeEvery
7
8
Spawns a saga on each action dispatched to the Store that matches the pattern. Allows concurrent handling of actions.
9
10
```typescript { .api }
11
/**
12
* Spawn saga on each matching action (allows concurrency)
13
* @param pattern - Action pattern to watch for
14
* @param worker - Saga function to spawn for each action
15
* @param args - Additional arguments passed to worker (action is always last)
16
* @returns ForkEffect that never completes (runs forever)
17
*/
18
function takeEvery<P extends ActionPattern>(
19
pattern: P,
20
worker: (action: ActionMatchingPattern<P>) => any
21
): ForkEffect<never>;
22
23
function takeEvery<P extends ActionPattern, Fn extends (...args: any[]) => any>(
24
pattern: P,
25
worker: Fn,
26
...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
27
): ForkEffect<never>;
28
29
/**
30
* Watch channel for messages
31
* @param channel - Channel to watch
32
* @param worker - Function to handle each message
33
* @returns ForkEffect
34
*/
35
function takeEvery<T>(
36
channel: TakeableChannel<T>,
37
worker: (item: T) => any
38
): ForkEffect<never>;
39
```
40
41
**Usage Examples:**
42
43
```typescript
44
import { takeEvery, call, put } from "redux-saga/effects";
45
46
function* fetchUser(action) {
47
try {
48
const user = yield call(api.fetchUser, action.payload.userId);
49
yield put({ type: 'USER_FETCH_SUCCEEDED', user });
50
} catch (error) {
51
yield put({ type: 'USER_FETCH_FAILED', error: error.message });
52
}
53
}
54
55
function* watchUserRequests() {
56
// Spawn fetchUser on every USER_FETCH_REQUESTED action
57
// Multiple requests can run concurrently
58
yield takeEvery('USER_FETCH_REQUESTED', fetchUser);
59
}
60
61
// With additional arguments
62
function* saveData(apiClient, action) {
63
yield call([apiClient, 'save'], action.payload);
64
}
65
66
function* watchSaveRequests() {
67
const apiClient = yield getContext('apiClient');
68
yield takeEvery('SAVE_REQUESTED', saveData, apiClient);
69
}
70
```
71
72
### takeLatest
73
74
Spawns a saga on each action, but automatically cancels any previous saga if it's still running. Only the latest saga runs.
75
76
```typescript { .api }
77
/**
78
* Spawn saga on each action, cancel previous if running (latest wins)
79
* @param pattern - Action pattern to watch for
80
* @param worker - Saga function to spawn
81
* @param args - Additional arguments passed to worker
82
* @returns ForkEffect that never completes
83
*/
84
function takeLatest<P extends ActionPattern>(
85
pattern: P,
86
worker: (action: ActionMatchingPattern<P>) => any
87
): ForkEffect<never>;
88
89
function takeLatest<P extends ActionPattern, Fn extends (...args: any[]) => any>(
90
pattern: P,
91
worker: Fn,
92
...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
93
): ForkEffect<never>;
94
95
function takeLatest<T>(
96
channel: TakeableChannel<T>,
97
worker: (item: T) => any
98
): ForkEffect<never>;
99
```
100
101
**Usage Examples:**
102
103
```typescript
104
import { takeLatest, call, put } from "redux-saga/effects";
105
106
function* searchUsers(action) {
107
try {
108
const results = yield call(api.searchUsers, action.payload.query);
109
yield put({ type: 'SEARCH_SUCCEEDED', results });
110
} catch (error) {
111
yield put({ type: 'SEARCH_FAILED', error: error.message });
112
}
113
}
114
115
function* watchSearchRequests() {
116
// Cancel previous search if user types quickly
117
// Only the latest search request will complete
118
yield takeLatest('SEARCH_REQUESTED', searchUsers);
119
}
120
```
121
122
### takeLeading
123
124
Spawns a saga on an action, then blocks until that saga completes before accepting new actions of the same pattern.
125
126
```typescript { .api }
127
/**
128
* Spawn saga and block until completion before accepting new actions
129
* @param pattern - Action pattern to watch for
130
* @param worker - Saga function to spawn
131
* @param args - Additional arguments passed to worker
132
* @returns ForkEffect that never completes
133
*/
134
function takeLeading<P extends ActionPattern>(
135
pattern: P,
136
worker: (action: ActionMatchingPattern<P>) => any
137
): ForkEffect<never>;
138
139
function takeLeading<P extends ActionPattern, Fn extends (...args: any[]) => any>(
140
pattern: P,
141
worker: Fn,
142
...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
143
): ForkEffect<never>;
144
145
function takeLeading<T>(
146
channel: TakeableChannel<T>,
147
worker: (item: T) => any
148
): ForkEffect<never>;
149
```
150
151
**Usage Examples:**
152
153
```typescript
154
import { takeLeading, call, put } from "redux-saga/effects";
155
156
function* processPayment(action) {
157
try {
158
const result = yield call(api.processPayment, action.payload);
159
yield put({ type: 'PAYMENT_SUCCEEDED', result });
160
} catch (error) {
161
yield put({ type: 'PAYMENT_FAILED', error: error.message });
162
}
163
}
164
165
function* watchPaymentRequests() {
166
// Ignore additional payment requests while one is processing
167
// Prevents double payments from rapid clicking
168
yield takeLeading('PROCESS_PAYMENT', processPayment);
169
}
170
```
171
172
### throttle
173
174
Spawns a saga on an action, then ignores subsequent actions for a specified time period while the saga is processing.
175
176
```typescript { .api }
177
/**
178
* Spawn saga and ignore actions for specified time period
179
* @param ms - Milliseconds to throttle (ignore subsequent actions)
180
* @param pattern - Action pattern to watch for
181
* @param worker - Saga function to spawn
182
* @param args - Additional arguments passed to worker
183
* @returns ForkEffect that never completes
184
*/
185
function throttle<P extends ActionPattern>(
186
ms: number,
187
pattern: P,
188
worker: (action: ActionMatchingPattern<P>) => any
189
): ForkEffect<never>;
190
191
function throttle<P extends ActionPattern, Fn extends (...args: any[]) => any>(
192
ms: number,
193
pattern: P,
194
worker: Fn,
195
...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
196
): ForkEffect<never>;
197
198
function throttle<T>(
199
ms: number,
200
channel: TakeableChannel<T>,
201
worker: (item: T) => any
202
): ForkEffect<never>;
203
```
204
205
**Usage Examples:**
206
207
```typescript
208
import { throttle, call, put } from "redux-saga/effects";
209
210
function* saveUserPreferences(action) {
211
try {
212
yield call(api.savePreferences, action.payload);
213
yield put({ type: 'PREFERENCES_SAVED' });
214
} catch (error) {
215
yield put({ type: 'PREFERENCES_SAVE_FAILED', error: error.message });
216
}
217
}
218
219
function* watchPreferenceChanges() {
220
// Save preferences but throttle to max once per 2 seconds
221
// Prevents excessive API calls when user changes multiple settings
222
yield throttle(2000, 'PREFERENCES_CHANGED', saveUserPreferences);
223
}
224
```
225
226
### debounce
227
228
Spawns a saga only after actions stop being dispatched for a specified time period. Resets the timer on each new action.
229
230
```typescript { .api }
231
/**
232
* Spawn saga after actions stop for specified time period
233
* @param ms - Milliseconds to wait after last action
234
* @param pattern - Action pattern to watch for
235
* @param worker - Saga function to spawn
236
* @param args - Additional arguments passed to worker
237
* @returns ForkEffect that never completes
238
*/
239
function debounce<P extends ActionPattern>(
240
ms: number,
241
pattern: P,
242
worker: (action: ActionMatchingPattern<P>) => any
243
): ForkEffect<never>;
244
245
function debounce<P extends ActionPattern, Fn extends (...args: any[]) => any>(
246
ms: number,
247
pattern: P,
248
worker: Fn,
249
...args: HelperWorkerParameters<ActionMatchingPattern<P>, Fn>
250
): ForkEffect<never>;
251
252
function debounce<T>(
253
ms: number,
254
channel: TakeableChannel<T>,
255
worker: (item: T) => any
256
): ForkEffect<never>;
257
```
258
259
**Usage Examples:**
260
261
```typescript
262
import { debounce, call, put } from "redux-saga/effects";
263
264
function* performAutoSave(action) {
265
try {
266
yield call(api.autoSave, action.payload.document);
267
yield put({ type: 'AUTO_SAVE_SUCCEEDED' });
268
} catch (error) {
269
yield put({ type: 'AUTO_SAVE_FAILED', error: error.message });
270
}
271
}
272
273
function* watchDocumentChanges() {
274
// Auto-save document 1 second after user stops typing
275
yield debounce(1000, 'DOCUMENT_CHANGED', performAutoSave);
276
}
277
278
function* searchSuggestions(action) {
279
const suggestions = yield call(api.getSearchSuggestions, action.payload.query);
280
yield put({ type: 'SUGGESTIONS_LOADED', suggestions });
281
}
282
283
function* watchSearchInput() {
284
// Fetch suggestions 300ms after user stops typing
285
yield debounce(300, 'SEARCH_INPUT_CHANGED', searchSuggestions);
286
}
287
```
288
289
### retry
290
291
Creates an effect that attempts to call a function multiple times with delays between attempts on failure.
292
293
```typescript { .api }
294
/**
295
* Retry function call with delay between attempts on failure
296
* @param maxTries - Maximum number of attempts
297
* @param delayLength - Milliseconds to wait between attempts
298
* @param fn - Function to retry
299
* @param args - Arguments to pass to function
300
* @returns CallEffect that resolves with successful result or throws final error
301
*/
302
function retry<Fn extends (...args: any[]) => any>(
303
maxTries: number,
304
delayLength: number,
305
fn: Fn,
306
...args: Parameters<Fn>
307
): CallEffect<SagaReturnType<Fn>>;
308
```
309
310
**Usage Examples:**
311
312
```typescript
313
import { retry, call, put } from "redux-saga/effects";
314
315
function* fetchUserWithRetry(action) {
316
try {
317
// Retry up to 3 times with 2 second delays
318
const user = yield retry(3, 2000, api.fetchUser, action.payload.userId);
319
yield put({ type: 'USER_FETCH_SUCCEEDED', user });
320
} catch (error) {
321
// All retries failed
322
yield put({ type: 'USER_FETCH_FAILED', error: error.message });
323
}
324
}
325
326
function* uploadFileWithRetry(action) {
327
try {
328
// Retry upload 5 times with 5 second delays
329
const result = yield retry(
330
5,
331
5000,
332
api.uploadFile,
333
action.payload.file,
334
action.payload.options
335
);
336
yield put({ type: 'UPLOAD_SUCCEEDED', result });
337
} catch (error) {
338
yield put({ type: 'UPLOAD_FAILED', error: error.message });
339
}
340
}
341
```
342
343
## Implementation Notes
344
345
Helper effects are high-level APIs built using lower-level effects:
346
347
- `takeEvery` = `fork(function*() { while(true) { const action = yield take(pattern); yield fork(worker, action); } })`
348
- `takeLatest` = Like takeEvery but with cancellation of previous task
349
- `takeLeading` = Like takeEvery but with blocking until completion
350
- `throttle` = takeEvery with timing-based action filtering
351
- `debounce` = takeEvery with timer reset on each action
352
353
These helpers provide common async patterns while maintaining full saga capabilities like cancellation and error handling.