@khanacademy/wonder-blocks-timing 4.0.1 → 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.
Files changed (50) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/components/with-action-scheduler.d.ts +4 -1
  3. package/dist/es/index.js +93 -105
  4. package/dist/hooks/use-interval.d.ts +27 -5
  5. package/dist/hooks/use-timeout.d.ts +27 -5
  6. package/dist/index.d.ts +2 -5
  7. package/dist/index.js +92 -105
  8. package/dist/util/animation-frame.d.ts +6 -6
  9. package/dist/util/interval.d.ts +8 -8
  10. package/dist/util/policies.d.ts +12 -8
  11. package/dist/util/timeout.d.ts +8 -7
  12. package/dist/util/types.d.ts +18 -10
  13. package/package.json +3 -3
  14. package/src/components/with-action-scheduler.tsx +9 -1
  15. package/src/hooks/__tests__/use-interval.test.ts +453 -56
  16. package/src/hooks/__tests__/use-timeout.test.ts +412 -58
  17. package/src/hooks/use-interval.ts +90 -25
  18. package/src/hooks/use-timeout.ts +90 -25
  19. package/src/index.ts +4 -14
  20. package/src/util/__tests__/animation-frame.test.ts +9 -10
  21. package/src/util/animation-frame.ts +12 -16
  22. package/src/util/interval.ts +13 -18
  23. package/src/util/policies.ts +13 -8
  24. package/src/util/timeout.ts +14 -18
  25. package/src/util/types.ts +19 -11
  26. package/tsconfig-build.json +2 -4
  27. package/tsconfig-build.tsbuildinfo +1 -1
  28. package/dist/components/action-scheduler-provider.js.flow +0 -33
  29. package/dist/components/with-action-scheduler.js.flow +0 -22
  30. package/dist/hooks/internal/use-updating-ref.d.ts +0 -13
  31. package/dist/hooks/internal/use-updating-ref.js.flow +0 -20
  32. package/dist/hooks/use-interval.js.flow +0 -17
  33. package/dist/hooks/use-scheduled-interval.d.ts +0 -2
  34. package/dist/hooks/use-scheduled-interval.js.flow +0 -12
  35. package/dist/hooks/use-scheduled-timeout.d.ts +0 -2
  36. package/dist/hooks/use-scheduled-timeout.js.flow +0 -12
  37. package/dist/hooks/use-timeout.js.flow +0 -17
  38. package/dist/index.js.flow +0 -31
  39. package/dist/util/action-scheduler.js.flow +0 -38
  40. package/dist/util/animation-frame.js.flow +0 -70
  41. package/dist/util/interval.js.flow +0 -69
  42. package/dist/util/policies.js.flow +0 -14
  43. package/dist/util/timeout.js.flow +0 -71
  44. package/dist/util/types.js.flow +0 -232
  45. package/dist/util/types.typestest.js.flow +0 -6
  46. package/src/hooks/__tests__/use-scheduled-interval.test.ts +0 -460
  47. package/src/hooks/__tests__/use-scheduled-timeout.test.ts +0 -478
  48. package/src/hooks/internal/use-updating-ref.ts +0 -23
  49. package/src/hooks/use-scheduled-interval.ts +0 -72
  50. package/src/hooks/use-scheduled-timeout.ts +0 -79
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @khanacademy/wonder-blocks-timing
2
2
 
3
+ ## 5.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - f72f7dd4: - **[BREAKING CHANGE]** Policy types are now using enums across all APIs
8
+ - **[BREAKING CHANGE]** `useScheduledTimeout` has been renamed to `useTimeout` and the original `useTimeout` has been removed. To update existing uses of `useTimeout` to the new API: if `active` was `true` just delete that argument or replace it with `{schedulingPolicy: SchedulingPolicy.Immediately}`; if `active` was `false` replace it with `{schedulingPolicy: SchedulingPolicy.OnDemand}`.
9
+ - **[BREAKING CHANGE]** `useScheduledInterval` has been renamed to `useInterval` and the original `useInterval` has been removed. To update existing uses of `useInterval` to the new API: if `active` was `true` just delete that argument or replace it with `{schedulingPolicy: SchedulingPolicy.Immediately}`; if `active` was `false` replace it with `{schedulingPolicy: SchedulingPolicy.OnDemand}`.
10
+ - **[NEW]** `useTimeout` now supports an optional `ActionPolicy` in the options. The default is to not reset the timeout when the `action` callback changes. This can be changed to `ActionPolicy.Reset` to reset the timeout when the `action` callback changes (it is recommended that you use `useCallback` on your `action` callback to avoid resetting the timeout everytime a component renders when using the `Reset` policy).
11
+ - **[NEW]** `useInterval` now supports an optional `ActionPolicy` in the options. The default is to not reset the interval when the `action` callback changes. This can be changed to `ActionPolicy.Reset` to reset the interval when the `action` callback changes (it is recommended that you use `useCallback` on your `action` callback to avoid resetting the interval everytime a component renders when using the `Reset` policy).
12
+ - **[BUGFIX]** `useTimeout` will now correctly reset the timeout when the `set` method is called, as intended.
13
+ - **[BUGFIX]** `useInterval` will now correctly reset the interval when the `set` method is called, as intended.
14
+
15
+ ## 4.0.2
16
+
17
+ ### Patch Changes
18
+
19
+ - 695f2567: Provide a friendly name (for dev tools) for withActionScheduler
20
+
3
21
  ## 4.0.1
4
22
 
5
23
  ### Patch Changes
@@ -10,5 +10,8 @@ type WithoutActionScheduler<T> = Omit<T, "schedule">;
10
10
  * the additional action scheduler prop. To attach the additional prop to
11
11
  * these props use the `WithActionScheduler` type.
12
12
  */
13
- export default function withActionScheduler<Props extends WithActionSchedulerProps>(WrappedComponent: React.ComponentType<Props>): (props: WithoutActionScheduler<Props>) => JSX.Element;
13
+ export default function withActionScheduler<Props extends WithActionSchedulerProps>(WrappedComponent: React.ComponentType<Props>): {
14
+ (props: WithoutActionScheduler<Props>): JSX.Element;
15
+ displayName: string;
16
+ };
14
17
  export {};
package/dist/es/index.js CHANGED
@@ -1,14 +1,21 @@
1
1
  import * as React from 'react';
2
- import { useRef, useEffect, useState, useCallback } from 'react';
2
+ import { useRef, useEffect, useMemo } from 'react';
3
3
 
4
- const SchedulePolicy = {
5
- Immediately: "schedule-immediately",
6
- OnDemand: "schedule-on-demand"
7
- };
8
- const ClearPolicy = {
9
- Resolve: "resolve-on-clear",
10
- Cancel: "cancel-on-clear"
11
- };
4
+ let SchedulePolicy = function (SchedulePolicy) {
5
+ SchedulePolicy["Immediately"] = "schedule-immediately";
6
+ SchedulePolicy["OnDemand"] = "schedule-on-demand";
7
+ return SchedulePolicy;
8
+ }({});
9
+ let ClearPolicy = function (ClearPolicy) {
10
+ ClearPolicy["Resolve"] = "resolve-on-clear";
11
+ ClearPolicy["Cancel"] = "cancel-on-clear";
12
+ return ClearPolicy;
13
+ }({});
14
+ let ActionPolicy = function (ActionPolicy) {
15
+ ActionPolicy["Reset"] = "reset";
16
+ ActionPolicy["Passive"] = "passive";
17
+ return ActionPolicy;
18
+ }({});
12
19
 
13
20
  class Timeout {
14
21
  constructor(action, timeoutMs, schedulePolicy = SchedulePolicy.Immediately) {
@@ -201,121 +208,102 @@ function _extends() {
201
208
  }
202
209
 
203
210
  function withActionScheduler(WrappedComponent) {
204
- return props => React.createElement(ActionSchedulerProvider, null, schedule => React.createElement(WrappedComponent, _extends({}, props, {
211
+ const displayName = `withActionScheduler(${WrappedComponent.displayName || WrappedComponent.name})`;
212
+ const C = props => React.createElement(ActionSchedulerProvider, null, schedule => React.createElement(WrappedComponent, _extends({}, props, {
205
213
  schedule: schedule
206
214
  })));
215
+ C.displayName = displayName;
216
+ return C;
207
217
  }
208
218
 
209
- const useUpdatingRef = value => {
210
- const ref = useRef(value);
211
- useEffect(() => {
212
- ref.current = value;
213
- }, [value]);
214
- return ref;
215
- };
216
-
217
- function useInterval(action, intervalMs, active) {
218
- const actionRef = useUpdatingRef(action);
219
- useEffect(() => {
220
- if (active) {
221
- const intervalId = setInterval(() => {
222
- actionRef.current();
223
- }, intervalMs);
224
- return () => {
225
- clearInterval(intervalId);
226
- };
227
- }
228
- }, [intervalMs, active, actionRef]);
229
- }
230
-
231
- function useTimeout(action, timeoutMs, active) {
232
- const actionRef = useUpdatingRef(action);
233
- useEffect(() => {
234
- if (active) {
235
- const timeoutId = setTimeout(() => {
236
- actionRef.current();
237
- }, timeoutMs);
238
- return () => {
239
- clearTimeout(timeoutId);
240
- };
241
- }
242
- }, [timeoutMs, active, actionRef]);
243
- }
244
-
245
- function useScheduledInterval(action, intervalMs, options) {
246
- var _options$schedulePoli;
219
+ function useInterval(action, intervalMs, options = {}) {
220
+ const {
221
+ actionPolicy,
222
+ clearPolicy,
223
+ schedulePolicy
224
+ } = options;
225
+ const actionProxyRef = useRef(action);
226
+ const intervalRef = useRef(null);
247
227
  if (typeof action !== "function") {
248
228
  throw new Error("Action must be a function");
249
229
  }
250
- if (intervalMs < 1) {
251
- throw new Error("Interval period must be >= 1");
252
- }
253
- const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : SchedulePolicy.Immediately;
254
- const [isSet, setIsSet] = useState(schedulePolicy === SchedulePolicy.Immediately);
255
- const set = useCallback(() => setIsSet(true), []);
256
- const actionRef = useUpdatingRef(action);
257
- const clear = useCallback(policy => {
258
- var _policy;
259
- policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
260
- if (isSet && policy === ClearPolicy.Resolve) {
261
- actionRef.current();
230
+ if (action !== actionProxyRef.current) {
231
+ actionProxyRef.current = action;
232
+ if (actionPolicy === ActionPolicy.Reset) {
233
+ var _intervalRef$current;
234
+ (_intervalRef$current = intervalRef.current) == null ? void 0 : _intervalRef$current.set();
262
235
  }
263
- setIsSet(false);
264
- }, [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
265
- const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
236
+ }
266
237
  useEffect(() => {
238
+ intervalRef.current = new Interval(() => {
239
+ actionProxyRef.current == null ? void 0 : actionProxyRef.current();
240
+ }, intervalMs, schedulePolicy);
267
241
  return () => {
268
- if (runOnUnmountRef.current) {
269
- actionRef.current();
270
- }
242
+ var _intervalRef$current2;
243
+ (_intervalRef$current2 = intervalRef.current) == null ? void 0 : _intervalRef$current2.clear(clearPolicy);
244
+ intervalRef.current = null;
271
245
  };
272
- }, []);
273
- useInterval(action, intervalMs, isSet);
274
- return {
275
- isSet,
276
- set,
277
- clear
278
- };
246
+ }, [intervalMs, clearPolicy, schedulePolicy]);
247
+ const externalApi = useMemo(() => ({
248
+ set: () => {
249
+ var _intervalRef$current3;
250
+ (_intervalRef$current3 = intervalRef.current) == null ? void 0 : _intervalRef$current3.set();
251
+ },
252
+ clear: policy => {
253
+ var _intervalRef$current4;
254
+ (_intervalRef$current4 = intervalRef.current) == null ? void 0 : _intervalRef$current4.clear(policy);
255
+ },
256
+ get isSet() {
257
+ var _intervalRef$current$, _intervalRef$current5;
258
+ return (_intervalRef$current$ = (_intervalRef$current5 = intervalRef.current) == null ? void 0 : _intervalRef$current5.isSet) != null ? _intervalRef$current$ : false;
259
+ }
260
+ }), []);
261
+ return externalApi;
279
262
  }
280
263
 
281
- function useScheduledTimeout(action, timeoutMs, options) {
282
- var _options$schedulePoli;
264
+ function useTimeout(action, timeoutMs, options = {}) {
265
+ const {
266
+ actionPolicy,
267
+ clearPolicy,
268
+ schedulePolicy
269
+ } = options;
270
+ const actionProxyRef = useRef(action);
271
+ const timeoutRef = useRef(null);
283
272
  if (typeof action !== "function") {
284
273
  throw new Error("Action must be a function");
285
274
  }
286
- if (timeoutMs < 0) {
287
- throw new Error("Timeout period must be >= 0");
288
- }
289
- const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : SchedulePolicy.Immediately;
290
- const [isSet, setIsSet] = useState(schedulePolicy === SchedulePolicy.Immediately);
291
- const set = useCallback(() => setIsSet(true), []);
292
- const wrappedAction = useCallback(() => {
293
- setIsSet(false);
294
- action();
295
- }, [action]);
296
- const actionRef = useUpdatingRef(wrappedAction);
297
- const clear = useCallback(policy => {
298
- var _policy;
299
- policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
300
- if (isSet && policy === ClearPolicy.Resolve) {
301
- actionRef.current();
275
+ if (action !== actionProxyRef.current) {
276
+ actionProxyRef.current = action;
277
+ if (actionPolicy === ActionPolicy.Reset) {
278
+ var _timeoutRef$current;
279
+ (_timeoutRef$current = timeoutRef.current) == null ? void 0 : _timeoutRef$current.set();
302
280
  }
303
- setIsSet(false);
304
- }, [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
305
- const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
281
+ }
306
282
  useEffect(() => {
283
+ timeoutRef.current = new Timeout(() => {
284
+ actionProxyRef.current == null ? void 0 : actionProxyRef.current();
285
+ }, timeoutMs, schedulePolicy);
307
286
  return () => {
308
- if (runOnUnmountRef.current) {
309
- actionRef.current();
310
- }
287
+ var _timeoutRef$current2;
288
+ (_timeoutRef$current2 = timeoutRef.current) == null ? void 0 : _timeoutRef$current2.clear(clearPolicy);
289
+ timeoutRef.current = null;
311
290
  };
312
- }, []);
313
- useTimeout(wrappedAction, timeoutMs, isSet);
314
- return {
315
- isSet,
316
- set,
317
- clear
318
- };
291
+ }, [timeoutMs, clearPolicy, schedulePolicy]);
292
+ const externalApi = useMemo(() => ({
293
+ set: () => {
294
+ var _timeoutRef$current3;
295
+ (_timeoutRef$current3 = timeoutRef.current) == null ? void 0 : _timeoutRef$current3.set();
296
+ },
297
+ clear: policy => {
298
+ var _timeoutRef$current4;
299
+ (_timeoutRef$current4 = timeoutRef.current) == null ? void 0 : _timeoutRef$current4.clear(policy);
300
+ },
301
+ get isSet() {
302
+ var _timeoutRef$current$i, _timeoutRef$current5;
303
+ return (_timeoutRef$current$i = (_timeoutRef$current5 = timeoutRef.current) == null ? void 0 : _timeoutRef$current5.isSet) != null ? _timeoutRef$current$i : false;
304
+ }
305
+ }), []);
306
+ return externalApi;
319
307
  }
320
308
 
321
- export { ActionSchedulerProvider, ClearPolicy, SchedulePolicy, useInterval, useScheduledInterval, useScheduledTimeout, useTimeout, withActionScheduler };
309
+ export { ActionPolicy, ActionSchedulerProvider, ClearPolicy, SchedulePolicy, useInterval, useTimeout, withActionScheduler };
@@ -1,8 +1,30 @@
1
+ import type { IInterval, HookOptions } from "../util/types";
1
2
  /**
2
- * A simple hook for using `setInterval`.
3
+ * Hook providing access to a scheduled interval.
3
4
  *
4
- * @param action called every `intervalMs` when `active` is true
5
- * @param intervalMs the duration between calls to `action`
6
- * @param active whether or not the interval is active
5
+ * @param action The action to be invoked each time the interval period has
6
+ * passed. By default, this will not cause the interval to restart if it
7
+ * changes. This makes it easier to use with inline lambda functions rather than
8
+ * requiring consumers to wrap their action in a `useCallback`. To change this
9
+ * behavior, see the `actionPolicy` option.
10
+ * @param intervalMs The interval period. If this changes, the interval will
11
+ * be reset per the `schedulePolicy` option.
12
+ * @param options Options for the hook.
13
+ * @param options.actionPolicy Determines how the action is handled when it
14
+ * changes. By default, the action is replaced but the interval is not reset,
15
+ * and the updated action will be invoked when the interval next fires.
16
+ * If you want to reset the interval when the action changes, use
17
+ * `ActionPolicy.Reset`.
18
+ * @param options.clearPolicy Determines how the interval is cleared when the
19
+ * component is unmounted or the interval is recreated. By default, the
20
+ * interval is cleared immediately. If you want to let the interval run to
21
+ * completion, use `ClearPolicy.Resolve`. This is NOT applied if the interval
22
+ * is cleared manually via the `clear()` method on the returned API.
23
+ * @param options.schedulePolicy Determines when the interval is scheduled.
24
+ * By default, the interval is scheduled immediately. If you want to delay
25
+ * scheduling the interval, use `SchedulePolicy.OnDemand`.
26
+ * @returns An `IInterval` API for interacting with the given interval. This
27
+ * API is a no-op if called when not mounted. This means that any calls prior
28
+ * to mounting or after unmounting will not have any effect.
7
29
  */
8
- export declare function useInterval(action: () => unknown, intervalMs: number, active: boolean): void;
30
+ export declare function useInterval(action: () => unknown, intervalMs: number, options?: HookOptions): IInterval;
@@ -1,8 +1,30 @@
1
+ import type { ITimeout, HookOptions } from "../util/types";
1
2
  /**
2
- * A simple hook for using `setTimeout`.
3
+ * Hook providing access to a scheduled timeout.
3
4
  *
4
- * @param action called after `timeoutMs` when `active` is true
5
- * @param timeoutMs the duration after which `action` is called
6
- * @param active whether or not the interval is active
5
+ * @param action The action to be invoked when the timeout period has
6
+ * passed. By default, this will not cause the timeout to restart if it changes.
7
+ * This makes it easier to use with inline lambda functions rather than
8
+ * requiring consumers to wrap their action in a `useCallback`. To change
9
+ * this behavior, see the `actionPolicy` option.
10
+ * @param timeoutMs The timeout period. If this changes, the timeout will
11
+ * be reset per the `schedulePolicy` option.
12
+ * @param options Options for the hook.
13
+ * @param options.actionPolicy Determines how the action is handled when it
14
+ * changes. By default, the action is replaced but the timeout is not reset,
15
+ * and the updated action will be invoked when the timeout next fires.
16
+ * If you want to reset the timeout when the action changes, use
17
+ * `ActionPolicy.Reset`.
18
+ * @param options.clearPolicy Determines how the timeout is cleared when the
19
+ * component is unmounted or the timeout is recreated. By default, the
20
+ * timeout is cleared immediately. If you want to let the timeout run to
21
+ * completion, use `ClearPolicy.Resolve`. This is NOT applied if the timeout
22
+ * is cleared manually via the `clear()` method on the returned API.
23
+ * @param options.schedulePolicy Determines when the timeout is scheduled.
24
+ * By default, the timeout is scheduled immediately. If you want to delay
25
+ * scheduling the timeout, use `SchedulePolicy.OnDemand`.
26
+ * @returns An `ITimeout` API for interacting with the given timeout. This
27
+ * API is a no-op if called when not mounted. This means that any calls prior
28
+ * to mounting or after unmounting will not have any effect.
7
29
  */
8
- export declare function useTimeout(action: () => unknown, timeoutMs: number, active: boolean): void;
30
+ export declare function useTimeout(action: () => unknown, timeoutMs: number, options?: HookOptions): ITimeout;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,6 @@
1
- import type { IAnimationFrame, IInterval, IScheduleActions, ITimeout, WithActionScheduler, WithActionSchedulerProps, WithoutActionScheduler } from "./util/types";
2
- export type { IAnimationFrame, IInterval, IScheduleActions, ITimeout, WithActionScheduler, WithActionSchedulerProps, WithoutActionScheduler, };
3
- export { SchedulePolicy, ClearPolicy } from "./util/policies";
1
+ export type { IAnimationFrame, IInterval, IScheduleActions, ITimeout, WithActionScheduler, WithActionSchedulerProps, WithoutActionScheduler, HookOptions, Options, } from "./util/types";
2
+ export { SchedulePolicy, ClearPolicy, ActionPolicy } from "./util/policies";
4
3
  export { default as ActionSchedulerProvider } from "./components/action-scheduler-provider";
5
4
  export { default as withActionScheduler } from "./components/with-action-scheduler";
6
5
  export { useInterval } from "./hooks/use-interval";
7
6
  export { useTimeout } from "./hooks/use-timeout";
8
- export { useScheduledInterval } from "./hooks/use-scheduled-interval";
9
- export { useScheduledTimeout } from "./hooks/use-scheduled-timeout";
package/dist/index.js CHANGED
@@ -24,14 +24,21 @@ function _interopNamespace(e) {
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
26
26
 
27
- const SchedulePolicy = {
28
- Immediately: "schedule-immediately",
29
- OnDemand: "schedule-on-demand"
30
- };
31
- const ClearPolicy = {
32
- Resolve: "resolve-on-clear",
33
- Cancel: "cancel-on-clear"
34
- };
27
+ let SchedulePolicy = function (SchedulePolicy) {
28
+ SchedulePolicy["Immediately"] = "schedule-immediately";
29
+ SchedulePolicy["OnDemand"] = "schedule-on-demand";
30
+ return SchedulePolicy;
31
+ }({});
32
+ let ClearPolicy = function (ClearPolicy) {
33
+ ClearPolicy["Resolve"] = "resolve-on-clear";
34
+ ClearPolicy["Cancel"] = "cancel-on-clear";
35
+ return ClearPolicy;
36
+ }({});
37
+ let ActionPolicy = function (ActionPolicy) {
38
+ ActionPolicy["Reset"] = "reset";
39
+ ActionPolicy["Passive"] = "passive";
40
+ return ActionPolicy;
41
+ }({});
35
42
 
36
43
  class Timeout {
37
44
  constructor(action, timeoutMs, schedulePolicy = SchedulePolicy.Immediately) {
@@ -224,128 +231,108 @@ function _extends() {
224
231
  }
225
232
 
226
233
  function withActionScheduler(WrappedComponent) {
227
- return props => React__namespace.createElement(ActionSchedulerProvider, null, schedule => React__namespace.createElement(WrappedComponent, _extends({}, props, {
234
+ const displayName = `withActionScheduler(${WrappedComponent.displayName || WrappedComponent.name})`;
235
+ const C = props => React__namespace.createElement(ActionSchedulerProvider, null, schedule => React__namespace.createElement(WrappedComponent, _extends({}, props, {
228
236
  schedule: schedule
229
237
  })));
238
+ C.displayName = displayName;
239
+ return C;
230
240
  }
231
241
 
232
- const useUpdatingRef = value => {
233
- const ref = React.useRef(value);
234
- React.useEffect(() => {
235
- ref.current = value;
236
- }, [value]);
237
- return ref;
238
- };
239
-
240
- function useInterval(action, intervalMs, active) {
241
- const actionRef = useUpdatingRef(action);
242
- React.useEffect(() => {
243
- if (active) {
244
- const intervalId = setInterval(() => {
245
- actionRef.current();
246
- }, intervalMs);
247
- return () => {
248
- clearInterval(intervalId);
249
- };
250
- }
251
- }, [intervalMs, active, actionRef]);
252
- }
253
-
254
- function useTimeout(action, timeoutMs, active) {
255
- const actionRef = useUpdatingRef(action);
256
- React.useEffect(() => {
257
- if (active) {
258
- const timeoutId = setTimeout(() => {
259
- actionRef.current();
260
- }, timeoutMs);
261
- return () => {
262
- clearTimeout(timeoutId);
263
- };
264
- }
265
- }, [timeoutMs, active, actionRef]);
266
- }
267
-
268
- function useScheduledInterval(action, intervalMs, options) {
269
- var _options$schedulePoli;
242
+ function useInterval(action, intervalMs, options = {}) {
243
+ const {
244
+ actionPolicy,
245
+ clearPolicy,
246
+ schedulePolicy
247
+ } = options;
248
+ const actionProxyRef = React.useRef(action);
249
+ const intervalRef = React.useRef(null);
270
250
  if (typeof action !== "function") {
271
251
  throw new Error("Action must be a function");
272
252
  }
273
- if (intervalMs < 1) {
274
- throw new Error("Interval period must be >= 1");
275
- }
276
- const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : SchedulePolicy.Immediately;
277
- const [isSet, setIsSet] = React.useState(schedulePolicy === SchedulePolicy.Immediately);
278
- const set = React.useCallback(() => setIsSet(true), []);
279
- const actionRef = useUpdatingRef(action);
280
- const clear = React.useCallback(policy => {
281
- var _policy;
282
- policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
283
- if (isSet && policy === ClearPolicy.Resolve) {
284
- actionRef.current();
253
+ if (action !== actionProxyRef.current) {
254
+ actionProxyRef.current = action;
255
+ if (actionPolicy === ActionPolicy.Reset) {
256
+ var _intervalRef$current;
257
+ (_intervalRef$current = intervalRef.current) == null ? void 0 : _intervalRef$current.set();
285
258
  }
286
- setIsSet(false);
287
- }, [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
288
- const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
259
+ }
289
260
  React.useEffect(() => {
261
+ intervalRef.current = new Interval(() => {
262
+ actionProxyRef.current == null ? void 0 : actionProxyRef.current();
263
+ }, intervalMs, schedulePolicy);
290
264
  return () => {
291
- if (runOnUnmountRef.current) {
292
- actionRef.current();
293
- }
265
+ var _intervalRef$current2;
266
+ (_intervalRef$current2 = intervalRef.current) == null ? void 0 : _intervalRef$current2.clear(clearPolicy);
267
+ intervalRef.current = null;
294
268
  };
295
- }, []);
296
- useInterval(action, intervalMs, isSet);
297
- return {
298
- isSet,
299
- set,
300
- clear
301
- };
269
+ }, [intervalMs, clearPolicy, schedulePolicy]);
270
+ const externalApi = React.useMemo(() => ({
271
+ set: () => {
272
+ var _intervalRef$current3;
273
+ (_intervalRef$current3 = intervalRef.current) == null ? void 0 : _intervalRef$current3.set();
274
+ },
275
+ clear: policy => {
276
+ var _intervalRef$current4;
277
+ (_intervalRef$current4 = intervalRef.current) == null ? void 0 : _intervalRef$current4.clear(policy);
278
+ },
279
+ get isSet() {
280
+ var _intervalRef$current$, _intervalRef$current5;
281
+ return (_intervalRef$current$ = (_intervalRef$current5 = intervalRef.current) == null ? void 0 : _intervalRef$current5.isSet) != null ? _intervalRef$current$ : false;
282
+ }
283
+ }), []);
284
+ return externalApi;
302
285
  }
303
286
 
304
- function useScheduledTimeout(action, timeoutMs, options) {
305
- var _options$schedulePoli;
287
+ function useTimeout(action, timeoutMs, options = {}) {
288
+ const {
289
+ actionPolicy,
290
+ clearPolicy,
291
+ schedulePolicy
292
+ } = options;
293
+ const actionProxyRef = React.useRef(action);
294
+ const timeoutRef = React.useRef(null);
306
295
  if (typeof action !== "function") {
307
296
  throw new Error("Action must be a function");
308
297
  }
309
- if (timeoutMs < 0) {
310
- throw new Error("Timeout period must be >= 0");
311
- }
312
- const schedulePolicy = (_options$schedulePoli = options == null ? void 0 : options.schedulePolicy) != null ? _options$schedulePoli : SchedulePolicy.Immediately;
313
- const [isSet, setIsSet] = React.useState(schedulePolicy === SchedulePolicy.Immediately);
314
- const set = React.useCallback(() => setIsSet(true), []);
315
- const wrappedAction = React.useCallback(() => {
316
- setIsSet(false);
317
- action();
318
- }, [action]);
319
- const actionRef = useUpdatingRef(wrappedAction);
320
- const clear = React.useCallback(policy => {
321
- var _policy;
322
- policy = (_policy = policy) != null ? _policy : options == null ? void 0 : options.clearPolicy;
323
- if (isSet && policy === ClearPolicy.Resolve) {
324
- actionRef.current();
298
+ if (action !== actionProxyRef.current) {
299
+ actionProxyRef.current = action;
300
+ if (actionPolicy === ActionPolicy.Reset) {
301
+ var _timeoutRef$current;
302
+ (_timeoutRef$current = timeoutRef.current) == null ? void 0 : _timeoutRef$current.set();
325
303
  }
326
- setIsSet(false);
327
- }, [actionRef, isSet, options == null ? void 0 : options.clearPolicy]);
328
- const runOnUnmountRef = useUpdatingRef(isSet && (options == null ? void 0 : options.clearPolicy) === ClearPolicy.Resolve);
304
+ }
329
305
  React.useEffect(() => {
306
+ timeoutRef.current = new Timeout(() => {
307
+ actionProxyRef.current == null ? void 0 : actionProxyRef.current();
308
+ }, timeoutMs, schedulePolicy);
330
309
  return () => {
331
- if (runOnUnmountRef.current) {
332
- actionRef.current();
333
- }
310
+ var _timeoutRef$current2;
311
+ (_timeoutRef$current2 = timeoutRef.current) == null ? void 0 : _timeoutRef$current2.clear(clearPolicy);
312
+ timeoutRef.current = null;
334
313
  };
335
- }, []);
336
- useTimeout(wrappedAction, timeoutMs, isSet);
337
- return {
338
- isSet,
339
- set,
340
- clear
341
- };
314
+ }, [timeoutMs, clearPolicy, schedulePolicy]);
315
+ const externalApi = React.useMemo(() => ({
316
+ set: () => {
317
+ var _timeoutRef$current3;
318
+ (_timeoutRef$current3 = timeoutRef.current) == null ? void 0 : _timeoutRef$current3.set();
319
+ },
320
+ clear: policy => {
321
+ var _timeoutRef$current4;
322
+ (_timeoutRef$current4 = timeoutRef.current) == null ? void 0 : _timeoutRef$current4.clear(policy);
323
+ },
324
+ get isSet() {
325
+ var _timeoutRef$current$i, _timeoutRef$current5;
326
+ return (_timeoutRef$current$i = (_timeoutRef$current5 = timeoutRef.current) == null ? void 0 : _timeoutRef$current5.isSet) != null ? _timeoutRef$current$i : false;
327
+ }
328
+ }), []);
329
+ return externalApi;
342
330
  }
343
331
 
332
+ exports.ActionPolicy = ActionPolicy;
344
333
  exports.ActionSchedulerProvider = ActionSchedulerProvider;
345
334
  exports.ClearPolicy = ClearPolicy;
346
335
  exports.SchedulePolicy = SchedulePolicy;
347
336
  exports.useInterval = useInterval;
348
- exports.useScheduledInterval = useScheduledInterval;
349
- exports.useScheduledTimeout = useScheduledTimeout;
350
337
  exports.useTimeout = useTimeout;
351
338
  exports.withActionScheduler = withActionScheduler;
@@ -1,4 +1,5 @@
1
- import type { IAnimationFrame, SchedulePolicy, ClearPolicy } from "./types";
1
+ import { SchedulePolicy, ClearPolicy } from "./policies";
2
+ import type { IAnimationFrame } from "./types";
2
3
  /**
3
4
  * Encapsulates everything associated with calling requestAnimationFrame/
4
5
  * cancelAnimationFrame, and managing the lifecycle of that request, including
@@ -15,8 +16,8 @@ export default class AnimationFrame implements IAnimationFrame {
15
16
  * Creates an animation frame request that will invoke the given action.
16
17
  * The request is not made until set is called.
17
18
  *
18
- * @param {(time: DOMHighResTimeStamp) => mixed} action The action to be invoked.
19
- * @param {SchedulePolicy} [schedulePolicy] When SchedulePolicy.Immediately,
19
+ * @param action The action to be invoked.
20
+ * @param [schedulePolicy] When SchedulePolicy.Immediately,
20
21
  * the interval is set immediately on instantiation; otherwise, `set` must be
21
22
  * called to set the interval.
22
23
  * Defaults to `SchedulePolicy.Immediately`.
@@ -47,15 +48,14 @@ export default class AnimationFrame implements IAnimationFrame {
47
48
  * If the request is pending, this cancels that pending request without
48
49
  * invoking the action. If no request is pending, this does nothing.
49
50
  *
50
- * @param {ClearPolicy} [policy] When ClearPolicy.Resolve, if the request
51
+ * @param [policy] When ClearPolicy.Resolve, if the request
51
52
  * was set when called, the request action is invoked after cancelling
52
53
  * the request; otherwise, the pending action is cancelled.
53
54
  * Defaults to `ClearPolicy.Cancel`.
54
- * @param {DOMHighResTimeStamp} [time] Timestamp to pass to the action when
55
+ * @param [time] Timestamp to pass to the action when
55
56
  * ClearPolicy.Resolve is specified. Ignored when ClearPolicy.Cancel is
56
57
  * specified.
57
58
  *
58
- * @returns {void}
59
59
  * @memberof AnimationFrame
60
60
  */
61
61
  clear(policy?: ClearPolicy, time?: DOMHighResTimeStamp): void;