or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-effects.mdchannels.mdconcurrency-effects.mdhelper-effects.mdindex.mdmiddleware.mdtesting.mdutilities.md

helper-effects.mddocs/

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.