@@ -9,23 +9,42 @@ yarn add redux-action-retry
9
9
# Getting Started
10
10
## Basic Configuration
11
11
12
- ``` typescript
13
- import {
14
- createRetryMechanism ,
15
- } from ' redux-action-retry' ;
12
+ ``` ts
13
+ import uuid from ' uuid/v4' ;
14
+ import { REDUX_ACTION_RETRY } from ' redux-action-retry' ;
16
15
16
+ // actions to retry
17
+ export const SEND_LOGS_TO_SERVER = ' SEND_LOGS_TO_SERVER' ;
18
+ export const NOTIFY_ACTION = ' NOTIFY_ACTION' ;
19
+
20
+ // action creators
21
+ export function sendLogsToServerActionCreator() {
22
+ return {
23
+ type: SEND_LOGS_TO_SERVER ,
24
+ // meta must be config
25
+ meta: {
26
+ [REDUX_ACTION_RETRY ]: {
27
+ // the id will be used to identify the action in the cache.
28
+ id: uuid ()
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ``` ts
17
36
import {
18
37
createStore ,
19
38
combineReducers ,
20
39
applyMiddleware ,
21
40
} from ' redux' ;
22
41
23
- // actions to retry
24
- const SEND_LOGS_TO_SERVER = ' SEND_LOGS_TO_SERVER' ;
25
- const NOTIFY_ACTION = ' NOTIFY_ACTION' ;
42
+ import { createRetryMechanism } from ' redux-action-retry' ;
43
+ import { SEND_LOGS_TO_SERVER , NOTIFY_ACTION } from ' ./actions' ;
26
44
27
- const { reducer, reduxActionRetryMiddleware , stateKeyName } = createRetryMechanism ({
45
+ const { reducer, reduxActionRetryMiddlewares , stateKeyName } = createRetryMechanism ({
28
46
cache: {
47
+ // action types are keys
29
48
[SEND_LOGS_TO_SERVER ]: {
30
49
type: SEND_LOGS_TO_SERVER ,
31
50
},
@@ -37,45 +56,302 @@ const { reducer, reduxActionRetryMiddleware, stateKeyName } = createRetryMechani
37
56
38
57
export const store = createStore (
39
58
combineReducers ({
59
+ // Use the stateKeyName as the key name of the reducer, as needed for the retry mechanism middlewares.
40
60
[stateKeyName ]: reducer
41
61
}),
42
62
applyMiddleware
43
63
(
44
- ... reduxActionRetryMiddleware ,
64
+ // Spread the middlewares
65
+ ... reduxActionRetryMiddlewares ,
45
66
)
46
67
);
47
68
```
48
69
49
- ## Basic Usage
70
+ ## Remove action from cache
50
71
51
72
When actions are dispatched, the middleware immediately caches the action, if the action is successful you can remove it dispatching a remove action.
52
73
53
- ``` typescript
74
+ ``` ts
54
75
import { put } from ' redux-saga/effects'
55
- import {
56
- removeActionCreator ,
57
- } from ' redux-action-retry' ;
76
+ import { removeActionCreator } from ' redux-action-retry' ;
58
77
59
78
function * sendLogsToServer(action ) {
60
- // domain logic
79
+ // domain logic...
80
+ // ...
61
81
62
- // all good
82
+ // if all good then remove from retry mechanism
63
83
yield put (removeActionCreator (action ))
64
84
}
65
85
```
66
86
67
- When retrying is needed you can dispatch a retry all action
87
+ ## Retry actions
88
+
89
+ When retrying is needed you can dispatch a retry all action.
68
90
69
- ``` typescript
91
+ ``` ts
70
92
import { put } from ' redux-saga/effects'
71
- import {
72
- retryAllActionCreator ,
73
- } from ' redux-action-retry' ;
93
+ import { retryAllActionCreator } from ' redux-action-retry' ;
74
94
75
95
function * appForeground() {
76
- // other actions
96
+ // domain logic...
97
+ // ...
77
98
78
99
// retry all cached actions
79
100
yield put (retryAllActionCreator ())
80
101
}
102
+ ```
103
+
104
+ ## Reset Store
105
+
106
+ ``` ts
107
+ import {
108
+ resetActionCreator ,
109
+ } from ' redux-action-retry' ;
110
+
111
+ function logout() {
112
+ // domain logic...
113
+ // ...
114
+
115
+ // reset the store for redux-action-retry
116
+ dispatch (resetActionCreator ())
117
+ }
118
+ ```
119
+
120
+ # Advanced Usage
121
+ ## Cooldown
122
+ ![ Without cooldown timeline] ( charts/basic_timeline.png )
123
+ Without cooldown timeline
124
+
125
+ ![ With cooldown timeline] ( charts/basic_cooldown_timeline.png )
126
+ With cooldown timeline
127
+
128
+
129
+ The amount of time an action could take executing (** Flying time** ) might be indicated, for example, by the timeout of a request, if a request is bound to fail after 30 seconds by timeout, then the action could be flying for at least 30 seconds.
130
+
131
+ Often when actions are dispatched, we wouldn't want them to be retried before we know they've succeeded or failed.
132
+
133
+
134
+ ### Configuration
135
+
136
+ ``` ts
137
+ export const REQUEST_TIMEOUT = duration (' PT30S' );
138
+ ```
139
+
140
+ ``` ts
141
+ import {
142
+ createStore ,
143
+ combineReducers ,
144
+ applyMiddleware ,
145
+ } from ' redux' ;
146
+ import { duration } from " moment" ;
147
+ import { createRetryMechanism , Cooldown } from ' redux-action-retry' ;
148
+ import { SEND_LOGS_TO_SERVER , NOTIFY_ACTION } from ' ./actions' ;
149
+
150
+ const { reducer, reduxActionRetryMiddlewares, stateKeyName } = createRetryMechanism ({
151
+ cache: {
152
+ [SEND_LOGS_TO_SERVER ]: {
153
+ type: SEND_LOGS_TO_SERVER ,
154
+ // cooldown config is per action
155
+ cooldownTime: duration (' PT31S' ),
156
+ },
157
+ [NOTIFY_ACTION ]: {
158
+ type: NOTIFY_ACTION ,
159
+ // As a rule of thumb add a bit more of time to allow for actual code and framework delays.
160
+ cooldownTime: duration (' PT31S' ),
161
+ },
162
+ extensions: [
163
+ Cooldown ,
164
+ ]
165
+ },
166
+ });
167
+
168
+ export const store = createStore (
169
+ combineReducers ({
170
+ [stateKeyName ]: reducer
171
+ }),
172
+ applyMiddleware
173
+ (
174
+ ... reduxActionRetryMiddlewares ,
175
+ )
176
+ );
177
+ ```
178
+
179
+ ### Early response
180
+
181
+ ![ Cancel cooldown timeline] ( charts/cancel_cooldown_timeline.png )
182
+ Cancel cooldown timeline
183
+
184
+ In the case of an early fail response we might want the action be to be retryable sooner than the cooldown time, so we might want to cancel it.
185
+
186
+ ``` ts
187
+ import { put } from ' redux-saga/effects'
188
+ import { cancelCooldownActionCreator } from ' redux-action-retry' ;
189
+
190
+ function * sendLogsToServer(action ) {
191
+ // domain logic...
192
+ // ...
193
+
194
+ if (allGood ) {
195
+ yield put (removeActionCreator (action ))
196
+ } else {
197
+ // in case of early fail
198
+ yield put (cancelCooldownActionCreator (action ))
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### Cool and Retry All utility
204
+
205
+ In case we want to Retry All without caring for the cooldown time, dispatch a ** coolAndRetryAllActionCreator** .
206
+
207
+ ``` ts
208
+ import { coolAndRetryAllActionCreator } from ' redux-action-retry' ;
209
+
210
+ function * rehydrate(action ) {
211
+ // domain logic...
212
+ // ...
213
+
214
+ yield put (coolAndRetryAllActionCreator (action ))
215
+ }
216
+ ```
217
+
218
+ ## Times
219
+
220
+ State without times:
221
+ ``` json
222
+ {"REDUX_ACTION_RETRY" : {
223
+ "cache" : [{
224
+ "action" : {
225
+ "type" : " SEND_LOGS_TO_SERVER" ,
226
+ "meta" : {
227
+ "REDUX_ACTION_RETRY" : {
228
+ "id" : " 3b2a7b5f-47f8-4774-af84-d28c5ecb61a7"
229
+ }
230
+ }
231
+ },
232
+ }]}}
233
+ ```
234
+ State with times:
235
+ ``` json
236
+ { "REDUX_ACTION_RETRY" : {
237
+ "cache" : [{
238
+ "action" : {
239
+ "type" : " SEND_LOGS_TO_SERVER" ,
240
+ "meta" : {
241
+ "REDUX_ACTION_RETRY" : {
242
+ "id" : " 3b2a7b5f-47f8-4774-af84-d28c5ecb61a7"
243
+ }
244
+ }
245
+ },
246
+ "times" : 5
247
+ }]}}
248
+ ```
249
+
250
+ In case you want to add a counter of the times an action have been retried, use the ** Times** Extension.
251
+
252
+ ### Configuration
253
+
254
+ ``` ts
255
+ import {
256
+ createStore ,
257
+ combineReducers ,
258
+ applyMiddleware ,
259
+ } from ' redux' ;
260
+ import { duration } from " moment" ;
261
+ import { createRetryMechanism , times } from ' redux-action-retry' ;
262
+ import { SEND_LOGS_TO_SERVER , NOTIFY_ACTION } from ' ./actions' ;
263
+
264
+ const { reducer, reduxActionRetryMiddlewares, stateKeyName } = createRetryMechanism ({
265
+ cache: {
266
+ [SEND_LOGS_TO_SERVER ]: {
267
+ type: SEND_LOGS_TO_SERVER ,
268
+ },
269
+ [NOTIFY_ACTION ]: {
270
+ type: NOTIFY_ACTION ,
271
+ },
272
+ extensions: [
273
+ times ,
274
+ ]
275
+ },
276
+ });
277
+
278
+ export const store = createStore (
279
+ combineReducers ({
280
+ [stateKeyName ]: reducer
281
+ }),
282
+ applyMiddleware
283
+ (
284
+ ... reduxActionRetryMiddlewares ,
285
+ )
286
+ );
287
+ ```
288
+
289
+ ### Use cases
290
+ #### Debugging / reports
291
+ In case of debugging or reading reports, having a count of how many times an action actually failed is very useful.
292
+
293
+ #### Optimistic response
294
+ We might want to keep triggering one saga but not the reducer, in those cases we could add a middleware to consume the action based on the times counter.
295
+ #### Cease retrying
296
+ In case we want to cease retrying after a number of attempts we could add a middleware that dispatches a remove command based on the times counter.
297
+
298
+ ## Time to live
299
+
300
+ In case we want to cease retrying after an amount of time, we could use ** Time to live** extension.
301
+
302
+ Dead actions are prevented from retrying, but when to remove those from the cache is up to the user (might want to send for stats/reports).
303
+
304
+ ### Configuration
305
+ ``` ts
306
+ import {
307
+ createStore ,
308
+ combineReducers ,
309
+ applyMiddleware ,
310
+ } from ' redux' ;
311
+ import { duration } from " moment" ;
312
+ import { createRetryMechanism , TimeToLive } from ' redux-action-retry' ;
313
+ import { SEND_LOGS_TO_SERVER , NOTIFY_ACTION } from ' ./actions' ;
314
+
315
+ const { reducer, reduxActionRetryMiddlewares, stateKeyName } = createRetryMechanism ({
316
+ cache: {
317
+ [SEND_LOGS_TO_SERVER ]: {
318
+ type: SEND_LOGS_TO_SERVER ,
319
+ // time to live config is per action
320
+ timeToLive: duration (' P3D' )
321
+ },
322
+ [NOTIFY_ACTION ]: {
323
+ type: NOTIFY_ACTION ,
324
+ cooldownTime: duration (' P1D' ),
325
+ },
326
+ extensions: [
327
+ TimeToLive ,
328
+ ]
329
+ },
330
+ });
331
+
332
+ export const store = createStore (
333
+ combineReducers ({
334
+ [stateKeyName ]: reducer
335
+ }),
336
+ applyMiddleware
337
+ (
338
+ ... reduxActionRetryMiddlewares ,
339
+ )
340
+ );
341
+ ```
342
+
343
+ ### How to remove dead actions
344
+
345
+ When we want to remove dead actions we dispatch a ** collectGarbageActionCreator** .
346
+ ``` ts
347
+ import { collectGarbageActionCreator } from ' redux-action-retry' ;
348
+
349
+ function * rehydrate(action ) {
350
+ // domain logic...
351
+ // ...
352
+
353
+ yield put (collectGarbageActionCreator (action ))
354
+ // we might want to collect garbage before issuing a retry all
355
+ yield put (coolAndRetryAllActionCreator (action ))
356
+ }
81
357
```
0 commit comments