@khanacademy/wonder-blocks-timing 4.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/es/index.js +89 -104
- package/dist/hooks/use-interval.d.ts +27 -5
- package/dist/hooks/use-timeout.d.ts +27 -5
- package/dist/index.d.ts +2 -5
- package/dist/index.js +88 -104
- package/dist/util/animation-frame.d.ts +6 -6
- package/dist/util/interval.d.ts +7 -7
- package/dist/util/policies.d.ts +12 -8
- package/dist/util/timeout.d.ts +7 -6
- package/dist/util/types.d.ts +18 -10
- package/package.json +1 -1
- package/src/hooks/__tests__/use-interval.test.ts +453 -56
- package/src/hooks/__tests__/use-timeout.test.ts +412 -58
- package/src/hooks/use-interval.ts +90 -25
- package/src/hooks/use-timeout.ts +90 -25
- package/src/index.ts +4 -14
- package/src/util/animation-frame.ts +12 -16
- package/src/util/interval.ts +12 -16
- package/src/util/policies.ts +13 -8
- package/src/util/timeout.ts +13 -16
- package/src/util/types.ts +19 -11
- package/tsconfig-build.json +2 -4
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/hooks/internal/use-updating-ref.d.ts +0 -13
- package/dist/hooks/use-scheduled-interval.d.ts +0 -2
- package/dist/hooks/use-scheduled-timeout.d.ts +0 -2
- package/src/hooks/__tests__/use-scheduled-interval.test.ts +0 -460
- package/src/hooks/__tests__/use-scheduled-timeout.test.ts +0 -478
- package/src/hooks/internal/use-updating-ref.ts +0 -23
- package/src/hooks/use-scheduled-interval.ts +0 -72
- package/src/hooks/use-scheduled-timeout.ts +0 -79
|
@@ -1,478 +0,0 @@
|
|
|
1
|
-
import {renderHook, act} from "@testing-library/react-hooks";
|
|
2
|
-
import {SchedulePolicy, ClearPolicy} from "../../util/policies";
|
|
3
|
-
|
|
4
|
-
import {useScheduledTimeout} from "../use-scheduled-timeout";
|
|
5
|
-
|
|
6
|
-
describe("useScheduledTimeout", () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
jest.useFakeTimers();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
jest.restoreAllMocks();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("throws if the action is not a function", () => {
|
|
16
|
-
// Arrange
|
|
17
|
-
|
|
18
|
-
// Act
|
|
19
|
-
const {result} = renderHook(() =>
|
|
20
|
-
useScheduledTimeout(null as any, 1000),
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
// Assert
|
|
24
|
-
expect(result.error).toEqual(Error("Action must be a function"));
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("throws if the period is less than 0", () => {
|
|
28
|
-
// Arrange
|
|
29
|
-
|
|
30
|
-
// Act
|
|
31
|
-
const {result} = renderHook(() => useScheduledTimeout(() => {}, -1));
|
|
32
|
-
|
|
33
|
-
// Assert
|
|
34
|
-
expect(result.error).toEqual(Error("Timeout period must be >= 0"));
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("should return an ITimeout", () => {
|
|
38
|
-
// Arrange
|
|
39
|
-
const {result} = renderHook(() => useScheduledTimeout(() => {}, 1000));
|
|
40
|
-
|
|
41
|
-
// Act
|
|
42
|
-
|
|
43
|
-
// Assert
|
|
44
|
-
expect(result.current).toEqual(
|
|
45
|
-
expect.objectContaining({
|
|
46
|
-
clear: expect.any(Function),
|
|
47
|
-
set: expect.any(Function),
|
|
48
|
-
isSet: expect.any(Boolean),
|
|
49
|
-
}),
|
|
50
|
-
);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("should default to being immediately set", () => {
|
|
54
|
-
// Arrange
|
|
55
|
-
const {result} = renderHook(() => useScheduledTimeout(() => {}, 1000));
|
|
56
|
-
|
|
57
|
-
// Act
|
|
58
|
-
|
|
59
|
-
// Assert
|
|
60
|
-
expect(result.current.isSet).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("should call the action before unmounting", () => {
|
|
64
|
-
const action = jest.fn();
|
|
65
|
-
const {unmount} = renderHook(() =>
|
|
66
|
-
useScheduledTimeout(action, 1000, {
|
|
67
|
-
clearPolicy: ClearPolicy.Resolve,
|
|
68
|
-
}),
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
act(() => {
|
|
72
|
-
unmount();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
expect(action).toHaveBeenCalled();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("should call the current action", () => {
|
|
79
|
-
// Arrange
|
|
80
|
-
const action1 = jest.fn();
|
|
81
|
-
const action2 = jest.fn();
|
|
82
|
-
const {rerender} = renderHook(
|
|
83
|
-
({action}: any) => useScheduledTimeout(action, 500),
|
|
84
|
-
{
|
|
85
|
-
initialProps: {action: action1},
|
|
86
|
-
},
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
// Act
|
|
90
|
-
rerender({action: action2});
|
|
91
|
-
jest.advanceTimersByTime(501);
|
|
92
|
-
|
|
93
|
-
// Assert
|
|
94
|
-
expect(action2).toHaveBeenCalledTimes(1);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("should only call setTimeout once even if action changes", () => {
|
|
98
|
-
// Arrange
|
|
99
|
-
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
100
|
-
const action1 = jest.fn();
|
|
101
|
-
const action2 = jest.fn();
|
|
102
|
-
const {rerender} = renderHook(
|
|
103
|
-
({action}: any) => useScheduledTimeout(action, 500),
|
|
104
|
-
{
|
|
105
|
-
initialProps: {action: action1},
|
|
106
|
-
},
|
|
107
|
-
);
|
|
108
|
-
// NOTE: For some reason setTimeout is called twice by the time we get
|
|
109
|
-
// here. I've verified that it only gets called once inside the hook
|
|
110
|
-
// so something else must be calling it.
|
|
111
|
-
const callCount = timeoutSpy.mock.calls.length;
|
|
112
|
-
|
|
113
|
-
// Act
|
|
114
|
-
rerender({action: action2});
|
|
115
|
-
|
|
116
|
-
// Assert
|
|
117
|
-
expect(timeoutSpy).toHaveBeenCalledTimes(callCount);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("should use the new timeout duration after changing it", () => {
|
|
121
|
-
// Arrange
|
|
122
|
-
const action = jest.fn();
|
|
123
|
-
const {rerender} = renderHook(
|
|
124
|
-
({timeoutMs}: any) => useScheduledTimeout(action, timeoutMs),
|
|
125
|
-
{
|
|
126
|
-
initialProps: {timeoutMs: 500},
|
|
127
|
-
},
|
|
128
|
-
);
|
|
129
|
-
rerender({timeoutMs: 1000});
|
|
130
|
-
|
|
131
|
-
// Act
|
|
132
|
-
jest.advanceTimersByTime(1501);
|
|
133
|
-
|
|
134
|
-
// Assert
|
|
135
|
-
expect(action).toHaveBeenCalledTimes(1);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("should restart the timeout if intervalMs changes", () => {
|
|
139
|
-
// Arrange
|
|
140
|
-
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
141
|
-
const {rerender} = renderHook(
|
|
142
|
-
({timeoutMs}: any) => useScheduledTimeout(() => {}, timeoutMs),
|
|
143
|
-
{
|
|
144
|
-
initialProps: {timeoutMs: 500},
|
|
145
|
-
},
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Act
|
|
149
|
-
rerender({timeoutMs: 1000});
|
|
150
|
-
|
|
151
|
-
// Assert
|
|
152
|
-
expect(timeoutSpy).toHaveBeenCalledWith(expect.any(Function), 500);
|
|
153
|
-
expect(timeoutSpy).toHaveBeenCalledWith(expect.any(Function), 1000);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe("SchedulePolicies.Immediately", () => {
|
|
157
|
-
it("should call the action after the timeout expires", () => {
|
|
158
|
-
// Arrange
|
|
159
|
-
const action = jest.fn();
|
|
160
|
-
renderHook(() => useScheduledTimeout(action, 1000));
|
|
161
|
-
|
|
162
|
-
// Act
|
|
163
|
-
act(() => {
|
|
164
|
-
jest.advanceTimersByTime(1000);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// Assert
|
|
168
|
-
expect(action).toHaveBeenCalled();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("should update isSet to false after the timeout expires", () => {
|
|
172
|
-
// Arrange
|
|
173
|
-
const action = jest.fn();
|
|
174
|
-
const {result} = renderHook(() =>
|
|
175
|
-
useScheduledTimeout(action, 1000),
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
// Act
|
|
179
|
-
act(() => {
|
|
180
|
-
jest.advanceTimersByTime(1001);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// Assert
|
|
184
|
-
expect(result.current.isSet).toBe(false);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("should call the action again if 'set' is called after the action was called", () => {
|
|
188
|
-
// Arrange
|
|
189
|
-
const action = jest.fn();
|
|
190
|
-
const {result} = renderHook(() =>
|
|
191
|
-
useScheduledTimeout(action, 1000),
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
// Act
|
|
195
|
-
act(() => {
|
|
196
|
-
jest.advanceTimersByTime(1001);
|
|
197
|
-
});
|
|
198
|
-
act(() => {
|
|
199
|
-
result.current.set();
|
|
200
|
-
});
|
|
201
|
-
act(() => {
|
|
202
|
-
jest.advanceTimersByTime(1001);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
// Assert
|
|
206
|
-
expect(action).toHaveBeenCalledTimes(2);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("should restart the timeout if timeoutMs gets updated", () => {
|
|
210
|
-
// Arrange
|
|
211
|
-
const action = jest.fn();
|
|
212
|
-
const {rerender} = renderHook(
|
|
213
|
-
({timeoutMs}: any) => useScheduledTimeout(action, timeoutMs),
|
|
214
|
-
{
|
|
215
|
-
initialProps: {timeoutMs: 1000},
|
|
216
|
-
},
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
// Act
|
|
220
|
-
act(() => {
|
|
221
|
-
jest.advanceTimersByTime(900);
|
|
222
|
-
});
|
|
223
|
-
rerender({timeoutMs: 500});
|
|
224
|
-
act(() => {
|
|
225
|
-
jest.advanceTimersByTime(100);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Assert
|
|
229
|
-
expect(action).not.toHaveBeenCalled();
|
|
230
|
-
act((): void => jest.advanceTimersByTime(500));
|
|
231
|
-
expect(action).toHaveBeenCalled();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("should should timeout after the new timeoutMs if it gets updated", () => {
|
|
235
|
-
// Arrange
|
|
236
|
-
const action = jest.fn();
|
|
237
|
-
const {rerender} = renderHook(
|
|
238
|
-
({timeoutMs}: any) => useScheduledTimeout(action, timeoutMs),
|
|
239
|
-
{
|
|
240
|
-
initialProps: {timeoutMs: 1000},
|
|
241
|
-
},
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
// Act
|
|
245
|
-
rerender({timeoutMs: 500});
|
|
246
|
-
act(() => {
|
|
247
|
-
jest.advanceTimersByTime(500);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// Assert
|
|
251
|
-
expect(action).toHaveBeenCalled();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it("should call the new action after re-rendering with a new action", () => {
|
|
255
|
-
// Arrange
|
|
256
|
-
const action1 = jest.fn();
|
|
257
|
-
const action2 = jest.fn();
|
|
258
|
-
const {rerender} = renderHook(
|
|
259
|
-
({action}: any) => useScheduledTimeout(action, 1000),
|
|
260
|
-
{
|
|
261
|
-
initialProps: {action: action1},
|
|
262
|
-
},
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
// Act
|
|
266
|
-
rerender({action: action2});
|
|
267
|
-
act(() => {
|
|
268
|
-
jest.advanceTimersByTime(1000);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// Assert
|
|
272
|
-
expect(action2).toHaveBeenCalled();
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it("should not call the original action after re-rendering with a new action", () => {
|
|
276
|
-
// Arrange
|
|
277
|
-
const action1 = jest.fn();
|
|
278
|
-
const action2 = jest.fn();
|
|
279
|
-
const {rerender} = renderHook(
|
|
280
|
-
({action}: any) => useScheduledTimeout(action, 1000),
|
|
281
|
-
{
|
|
282
|
-
initialProps: {action: action1},
|
|
283
|
-
},
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
// Act
|
|
287
|
-
rerender({action: action2});
|
|
288
|
-
act(() => {
|
|
289
|
-
jest.advanceTimersByTime(1000);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Assert
|
|
293
|
-
expect(action1).not.toHaveBeenCalled();
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it("should not call the action if the timeout is cleared", () => {
|
|
297
|
-
// Arrange
|
|
298
|
-
const action = jest.fn();
|
|
299
|
-
const {result} = renderHook(() =>
|
|
300
|
-
useScheduledTimeout(action, 1000),
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
// Act
|
|
304
|
-
act(() => {
|
|
305
|
-
result.current.clear();
|
|
306
|
-
});
|
|
307
|
-
act(() => {
|
|
308
|
-
jest.advanceTimersByTime(1000);
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
// Assert
|
|
312
|
-
expect(action).not.toHaveBeenCalled();
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it("should call the action when the timeout is cleared when passing ClearPolicies.Resolve to clear()", () => {
|
|
316
|
-
// Arrange
|
|
317
|
-
const action = jest.fn();
|
|
318
|
-
const {result} = renderHook(() =>
|
|
319
|
-
useScheduledTimeout(action, 1000),
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Act
|
|
323
|
-
act(() => {
|
|
324
|
-
result.current.clear(ClearPolicy.Resolve);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
// Assert
|
|
328
|
-
expect(action).toHaveBeenCalled();
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it("should call the action when the timeout is cleared when using ClearPolicies.Resolve in options", () => {
|
|
332
|
-
// Arrange
|
|
333
|
-
const action = jest.fn();
|
|
334
|
-
const {result} = renderHook(() =>
|
|
335
|
-
useScheduledTimeout(action, 1000, {
|
|
336
|
-
clearPolicy: ClearPolicy.Resolve,
|
|
337
|
-
}),
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
// Act
|
|
341
|
-
act(() => {
|
|
342
|
-
result.current.clear();
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
// Assert
|
|
346
|
-
expect(action).toHaveBeenCalled();
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it("should call the action on unmount when using ClearPolicies.Resolve in options", () => {
|
|
350
|
-
// Arrange
|
|
351
|
-
const action = jest.fn();
|
|
352
|
-
const {unmount} = renderHook(() =>
|
|
353
|
-
useScheduledTimeout(action, 1000, {
|
|
354
|
-
clearPolicy: ClearPolicy.Resolve,
|
|
355
|
-
}),
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
// Act
|
|
359
|
-
unmount();
|
|
360
|
-
|
|
361
|
-
// Assert
|
|
362
|
-
expect(action).toHaveBeenCalled();
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it("should not call the action on unmount when using the default options", () => {
|
|
366
|
-
// Arrange
|
|
367
|
-
const action = jest.fn();
|
|
368
|
-
const {unmount} = renderHook(() =>
|
|
369
|
-
useScheduledTimeout(action, 1000),
|
|
370
|
-
);
|
|
371
|
-
|
|
372
|
-
// Act
|
|
373
|
-
unmount();
|
|
374
|
-
|
|
375
|
-
// Assert
|
|
376
|
-
expect(action).not.toHaveBeenCalled();
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
describe("SchedulePolicies.OnDemand", () => {
|
|
381
|
-
it("should not set the timer on creation", () => {
|
|
382
|
-
// Arrange
|
|
383
|
-
const {result} = renderHook(() =>
|
|
384
|
-
useScheduledTimeout(() => {}, 1000, {
|
|
385
|
-
schedulePolicy: SchedulePolicy.OnDemand,
|
|
386
|
-
}),
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
// Act
|
|
390
|
-
|
|
391
|
-
// Assert
|
|
392
|
-
expect(result.current.isSet).toBe(false);
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it("should not call action after timeoutMs if the timer hasn't been set", () => {
|
|
396
|
-
// Arrange
|
|
397
|
-
const action = jest.fn();
|
|
398
|
-
renderHook(() =>
|
|
399
|
-
useScheduledTimeout(action, 1000, {
|
|
400
|
-
schedulePolicy: SchedulePolicy.OnDemand,
|
|
401
|
-
}),
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
// Act
|
|
405
|
-
act(() => {
|
|
406
|
-
jest.advanceTimersByTime(1000);
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
// Assert
|
|
410
|
-
expect(action).not.toHaveBeenCalled();
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it("should call action after timeoutMs if the timer has been set", () => {
|
|
414
|
-
// Arrange
|
|
415
|
-
const action = jest.fn();
|
|
416
|
-
const {result} = renderHook(() =>
|
|
417
|
-
useScheduledTimeout(action, 1000, {
|
|
418
|
-
schedulePolicy: SchedulePolicy.OnDemand,
|
|
419
|
-
}),
|
|
420
|
-
);
|
|
421
|
-
|
|
422
|
-
// Act
|
|
423
|
-
act(() => {
|
|
424
|
-
result.current.set();
|
|
425
|
-
});
|
|
426
|
-
act(() => {
|
|
427
|
-
jest.advanceTimersByTime(1000);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
// Assert
|
|
431
|
-
expect(action).toHaveBeenCalled();
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
it("should reset the timer after calling set() again", () => {
|
|
435
|
-
// Arrange
|
|
436
|
-
const action = jest.fn();
|
|
437
|
-
const {result} = renderHook(() =>
|
|
438
|
-
useScheduledTimeout(action, 1000, {
|
|
439
|
-
schedulePolicy: SchedulePolicy.OnDemand,
|
|
440
|
-
}),
|
|
441
|
-
);
|
|
442
|
-
|
|
443
|
-
// Act
|
|
444
|
-
act(() => {
|
|
445
|
-
result.current.set();
|
|
446
|
-
jest.advanceTimersByTime(500);
|
|
447
|
-
result.current.set();
|
|
448
|
-
jest.advanceTimersByTime(500);
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
// Assert
|
|
452
|
-
expect(action).not.toHaveBeenCalled();
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
it("should call the action after calling set() again", () => {
|
|
456
|
-
// Arrange
|
|
457
|
-
const action = jest.fn();
|
|
458
|
-
const {result} = renderHook(() =>
|
|
459
|
-
useScheduledTimeout(action, 1000, {
|
|
460
|
-
schedulePolicy: SchedulePolicy.OnDemand,
|
|
461
|
-
}),
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
// Act
|
|
465
|
-
act(() => {
|
|
466
|
-
result.current.set();
|
|
467
|
-
jest.advanceTimersByTime(500);
|
|
468
|
-
});
|
|
469
|
-
act(() => {
|
|
470
|
-
result.current.set();
|
|
471
|
-
jest.advanceTimersByTime(1000);
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// Assert
|
|
475
|
-
expect(action).toHaveBeenCalled();
|
|
476
|
-
});
|
|
477
|
-
});
|
|
478
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {useEffect, useRef} from "react";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns a ref whose .current value is updated whenever
|
|
5
|
-
* the `value` passed to this hook changes.
|
|
6
|
-
*
|
|
7
|
-
* this is great for values that you want to reference from
|
|
8
|
-
* within a useCallback or useEffect event listener, without
|
|
9
|
-
* re-triggering the effect when the value changes
|
|
10
|
-
*
|
|
11
|
-
* @returns {{current: T}}
|
|
12
|
-
*/
|
|
13
|
-
export const useUpdatingRef = <T>(
|
|
14
|
-
value: T,
|
|
15
|
-
): {
|
|
16
|
-
current: T;
|
|
17
|
-
} => {
|
|
18
|
-
const ref = useRef<T>(value);
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
ref.current = value;
|
|
21
|
-
}, [value]);
|
|
22
|
-
return ref;
|
|
23
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import {useEffect, useState, useCallback} from "react";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
SchedulePolicy as SchedulePolicies,
|
|
5
|
-
ClearPolicy as ClearPolicies,
|
|
6
|
-
} from "../util/policies";
|
|
7
|
-
import type {IInterval, ClearPolicy, Options} from "../util/types";
|
|
8
|
-
|
|
9
|
-
import {useUpdatingRef} from "./internal/use-updating-ref";
|
|
10
|
-
import {useInterval} from "./use-interval";
|
|
11
|
-
|
|
12
|
-
export function useScheduledInterval(
|
|
13
|
-
action: () => unknown,
|
|
14
|
-
intervalMs: number,
|
|
15
|
-
options?: Options,
|
|
16
|
-
): IInterval {
|
|
17
|
-
if (typeof action !== "function") {
|
|
18
|
-
throw new Error("Action must be a function");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (intervalMs < 1) {
|
|
22
|
-
throw new Error("Interval period must be >= 1");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const schedulePolicy =
|
|
26
|
-
options?.schedulePolicy ?? SchedulePolicies.Immediately;
|
|
27
|
-
|
|
28
|
-
const [isSet, setIsSet] = useState(
|
|
29
|
-
schedulePolicy === SchedulePolicies.Immediately,
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const set = useCallback(() => setIsSet(true), []);
|
|
33
|
-
|
|
34
|
-
const actionRef = useUpdatingRef(action);
|
|
35
|
-
|
|
36
|
-
const clear = useCallback(
|
|
37
|
-
(policy?: ClearPolicy) => {
|
|
38
|
-
policy = policy ?? options?.clearPolicy;
|
|
39
|
-
if (isSet && policy === ClearPolicies.Resolve) {
|
|
40
|
-
actionRef.current();
|
|
41
|
-
}
|
|
42
|
-
setIsSet(false);
|
|
43
|
-
},
|
|
44
|
-
// react-hooks/exhaustive-deps doesn't require refs to be
|
|
45
|
-
// listed in the deps array. Unfortunately, in this situation
|
|
46
|
-
// it doesn't recognized actionRef as a ref.
|
|
47
|
-
[actionRef, isSet, options?.clearPolicy],
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
const runOnUnmountRef = useUpdatingRef(
|
|
51
|
-
isSet && options?.clearPolicy === ClearPolicies.Resolve,
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
return () => {
|
|
56
|
-
// This code will only run with the component using this
|
|
57
|
-
// hook is unmounted.
|
|
58
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59
|
-
if (runOnUnmountRef.current) {
|
|
60
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
61
|
-
actionRef.current();
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
// This eslint rule doesn't realize actionRef and runOnUnmountRef
|
|
65
|
-
// a both refs and thus do not have to be listed as deps.
|
|
66
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
|
-
}, []);
|
|
68
|
-
|
|
69
|
-
useInterval(action, intervalMs, isSet);
|
|
70
|
-
|
|
71
|
-
return {isSet, set, clear};
|
|
72
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import {useEffect, useState, useCallback} from "react";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
SchedulePolicy as SchedulePolicies,
|
|
5
|
-
ClearPolicy as ClearPolicies,
|
|
6
|
-
} from "../util/policies";
|
|
7
|
-
import type {ITimeout, ClearPolicy, Options} from "../util/types";
|
|
8
|
-
|
|
9
|
-
import {useUpdatingRef} from "./internal/use-updating-ref";
|
|
10
|
-
import {useTimeout} from "./use-timeout";
|
|
11
|
-
|
|
12
|
-
export function useScheduledTimeout(
|
|
13
|
-
action: () => unknown,
|
|
14
|
-
timeoutMs: number,
|
|
15
|
-
options?: Options,
|
|
16
|
-
): ITimeout {
|
|
17
|
-
if (typeof action !== "function") {
|
|
18
|
-
throw new Error("Action must be a function");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (timeoutMs < 0) {
|
|
22
|
-
throw new Error("Timeout period must be >= 0");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const schedulePolicy =
|
|
26
|
-
options?.schedulePolicy ?? SchedulePolicies.Immediately;
|
|
27
|
-
|
|
28
|
-
const [isSet, setIsSet] = useState(
|
|
29
|
-
schedulePolicy === SchedulePolicies.Immediately,
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const set = useCallback(() => setIsSet(true), []);
|
|
33
|
-
|
|
34
|
-
// This wrapper isn't present in useScheduledInterval because we
|
|
35
|
-
// don't need to update `isSet` in that situations.
|
|
36
|
-
const wrappedAction = useCallback(() => {
|
|
37
|
-
setIsSet(false);
|
|
38
|
-
action();
|
|
39
|
-
}, [action]);
|
|
40
|
-
|
|
41
|
-
const actionRef = useUpdatingRef(wrappedAction);
|
|
42
|
-
|
|
43
|
-
const clear = useCallback(
|
|
44
|
-
(policy?: ClearPolicy) => {
|
|
45
|
-
policy = policy ?? options?.clearPolicy;
|
|
46
|
-
if (isSet && policy === ClearPolicies.Resolve) {
|
|
47
|
-
actionRef.current();
|
|
48
|
-
}
|
|
49
|
-
setIsSet(false);
|
|
50
|
-
},
|
|
51
|
-
// react-hooks/exhaustive-deps doesn't require refs to be
|
|
52
|
-
// listed in the deps array. Unfortunately, in this situation
|
|
53
|
-
// it doesn't recognized actionRef as a ref.
|
|
54
|
-
[actionRef, isSet, options?.clearPolicy],
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const runOnUnmountRef = useUpdatingRef(
|
|
58
|
-
isSet && options?.clearPolicy === ClearPolicies.Resolve,
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
return () => {
|
|
63
|
-
// This code will only run with the component using this
|
|
64
|
-
// hook is unmounted.
|
|
65
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
66
|
-
if (runOnUnmountRef.current) {
|
|
67
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
-
actionRef.current();
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
// This eslint rule doesn't realize actionRef and runOnUnmountRef
|
|
72
|
-
// a both refs and thus do not have to be listed as deps.
|
|
73
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
-
}, []);
|
|
75
|
-
|
|
76
|
-
useTimeout(wrappedAction, timeoutMs, isSet);
|
|
77
|
-
|
|
78
|
-
return {isSet, set, clear};
|
|
79
|
-
}
|