@tanstack/preact-pacer 0.18.0 → 0.19.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 (103) hide show
  1. package/dist/async-batcher/index.cjs +3 -3
  2. package/dist/async-batcher/index.d.cts +2 -2
  3. package/dist/async-batcher/index.d.ts +2 -2
  4. package/dist/async-batcher/useAsyncBatcher.cjs +35 -11
  5. package/dist/async-batcher/useAsyncBatcher.cjs.map +1 -1
  6. package/dist/async-batcher/useAsyncBatcher.d.cts +45 -10
  7. package/dist/async-batcher/useAsyncBatcher.d.ts +45 -10
  8. package/dist/async-batcher/useAsyncBatcher.js +32 -8
  9. package/dist/async-batcher/useAsyncBatcher.js.map +1 -1
  10. package/dist/async-debouncer/index.cjs +3 -3
  11. package/dist/async-debouncer/index.d.cts +2 -2
  12. package/dist/async-debouncer/index.d.ts +2 -2
  13. package/dist/async-debouncer/useAsyncDebouncer.cjs +35 -11
  14. package/dist/async-debouncer/useAsyncDebouncer.cjs.map +1 -1
  15. package/dist/async-debouncer/useAsyncDebouncer.d.cts +45 -10
  16. package/dist/async-debouncer/useAsyncDebouncer.d.ts +45 -10
  17. package/dist/async-debouncer/useAsyncDebouncer.js +32 -8
  18. package/dist/async-debouncer/useAsyncDebouncer.js.map +1 -1
  19. package/dist/async-queuer/index.cjs +3 -3
  20. package/dist/async-queuer/index.d.cts +2 -2
  21. package/dist/async-queuer/index.d.ts +2 -2
  22. package/dist/async-queuer/useAsyncQueuedState.cjs.map +1 -1
  23. package/dist/async-queuer/useAsyncQueuedState.d.cts +2 -2
  24. package/dist/async-queuer/useAsyncQueuedState.d.ts +2 -2
  25. package/dist/async-queuer/useAsyncQueuedState.js.map +1 -1
  26. package/dist/async-queuer/useAsyncQueuer.cjs +35 -11
  27. package/dist/async-queuer/useAsyncQueuer.cjs.map +1 -1
  28. package/dist/async-queuer/useAsyncQueuer.d.cts +45 -10
  29. package/dist/async-queuer/useAsyncQueuer.d.ts +45 -10
  30. package/dist/async-queuer/useAsyncQueuer.js +32 -8
  31. package/dist/async-queuer/useAsyncQueuer.js.map +1 -1
  32. package/dist/async-rate-limiter/index.cjs +3 -3
  33. package/dist/async-rate-limiter/index.d.cts +2 -2
  34. package/dist/async-rate-limiter/index.d.ts +2 -2
  35. package/dist/async-rate-limiter/useAsyncRateLimiter.cjs +35 -11
  36. package/dist/async-rate-limiter/useAsyncRateLimiter.cjs.map +1 -1
  37. package/dist/async-rate-limiter/useAsyncRateLimiter.d.cts +45 -10
  38. package/dist/async-rate-limiter/useAsyncRateLimiter.d.ts +45 -10
  39. package/dist/async-rate-limiter/useAsyncRateLimiter.js +32 -8
  40. package/dist/async-rate-limiter/useAsyncRateLimiter.js.map +1 -1
  41. package/dist/async-retryer/index.cjs +3 -3
  42. package/dist/async-throttler/index.cjs +3 -3
  43. package/dist/async-throttler/index.d.cts +2 -2
  44. package/dist/async-throttler/index.d.ts +2 -2
  45. package/dist/async-throttler/useAsyncThrottler.cjs +35 -11
  46. package/dist/async-throttler/useAsyncThrottler.cjs.map +1 -1
  47. package/dist/async-throttler/useAsyncThrottler.d.cts +45 -10
  48. package/dist/async-throttler/useAsyncThrottler.d.ts +45 -10
  49. package/dist/async-throttler/useAsyncThrottler.js +32 -8
  50. package/dist/async-throttler/useAsyncThrottler.js.map +1 -1
  51. package/dist/batcher/index.cjs +3 -3
  52. package/dist/batcher/useBatcher.cjs +35 -11
  53. package/dist/batcher/useBatcher.cjs.map +1 -1
  54. package/dist/batcher/useBatcher.d.cts +42 -7
  55. package/dist/batcher/useBatcher.d.ts +42 -7
  56. package/dist/batcher/useBatcher.js +32 -8
  57. package/dist/batcher/useBatcher.js.map +1 -1
  58. package/dist/debouncer/index.cjs +3 -3
  59. package/dist/debouncer/useDebouncer.cjs +35 -11
  60. package/dist/debouncer/useDebouncer.cjs.map +1 -1
  61. package/dist/debouncer/useDebouncer.d.cts +42 -7
  62. package/dist/debouncer/useDebouncer.d.ts +42 -7
  63. package/dist/debouncer/useDebouncer.js +32 -8
  64. package/dist/debouncer/useDebouncer.js.map +1 -1
  65. package/dist/index.cjs +3 -3
  66. package/dist/index.d.cts +6 -6
  67. package/dist/index.d.ts +6 -6
  68. package/dist/provider/PacerProvider.d.cts +1 -1
  69. package/dist/queuer/index.cjs +3 -3
  70. package/dist/queuer/useQueuer.cjs +35 -11
  71. package/dist/queuer/useQueuer.cjs.map +1 -1
  72. package/dist/queuer/useQueuer.d.cts +42 -7
  73. package/dist/queuer/useQueuer.d.ts +42 -7
  74. package/dist/queuer/useQueuer.js +32 -8
  75. package/dist/queuer/useQueuer.js.map +1 -1
  76. package/dist/rate-limiter/index.cjs +3 -3
  77. package/dist/rate-limiter/useRateLimiter.cjs +35 -11
  78. package/dist/rate-limiter/useRateLimiter.cjs.map +1 -1
  79. package/dist/rate-limiter/useRateLimiter.d.cts +42 -7
  80. package/dist/rate-limiter/useRateLimiter.d.ts +42 -7
  81. package/dist/rate-limiter/useRateLimiter.js +32 -8
  82. package/dist/rate-limiter/useRateLimiter.js.map +1 -1
  83. package/dist/throttler/index.cjs +3 -3
  84. package/dist/throttler/useThrottler.cjs +35 -11
  85. package/dist/throttler/useThrottler.cjs.map +1 -1
  86. package/dist/throttler/useThrottler.d.cts +42 -7
  87. package/dist/throttler/useThrottler.d.ts +42 -7
  88. package/dist/throttler/useThrottler.js +32 -8
  89. package/dist/throttler/useThrottler.js.map +1 -1
  90. package/dist/types/index.cjs +3 -3
  91. package/dist/utils/index.cjs +3 -3
  92. package/package.json +1 -1
  93. package/src/async-batcher/useAsyncBatcher.ts +64 -13
  94. package/src/async-debouncer/useAsyncDebouncer.ts +64 -13
  95. package/src/async-queuer/useAsyncQueuedState.ts +2 -2
  96. package/src/async-queuer/useAsyncQueuer.ts +64 -13
  97. package/src/async-rate-limiter/useAsyncRateLimiter.ts +64 -13
  98. package/src/async-throttler/useAsyncThrottler.ts +64 -13
  99. package/src/batcher/useBatcher.ts +61 -8
  100. package/src/debouncer/useDebouncer.ts +61 -8
  101. package/src/queuer/useQueuer.ts +61 -8
  102. package/src/rate-limiter/useRateLimiter.ts +61 -8
  103. package/src/throttler/useThrottler.ts +61 -8
@@ -1,7 +1,7 @@
1
1
  const require_PacerProvider = require('../provider/PacerProvider.cjs');
2
2
  let preact_hooks = require("preact/hooks");
3
- let __tanstack_preact_store = require("@tanstack/preact-store");
4
- let __tanstack_pacer_batcher = require("@tanstack/pacer/batcher");
3
+ let _tanstack_preact_store = require("@tanstack/preact-store");
4
+ let _tanstack_pacer_batcher = require("@tanstack/pacer/batcher");
5
5
 
6
6
  //#region src/batcher/useBatcher.ts
7
7
  /**
@@ -18,14 +18,24 @@ let __tanstack_pacer_batcher = require("@tanstack/pacer/batcher");
18
18
  *
19
19
  * ## State Management and Selector
20
20
  *
21
- * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
22
- * to specify which state changes will trigger a re-render, optimizing performance by preventing
23
- * unnecessary re-renders when irrelevant state changes occur.
21
+ * The hook uses TanStack Store for reactive state management. You can subscribe to state changes
22
+ * in two ways:
23
+ *
24
+ * **1. Using `batcher.Subscribe` HOC (Recommended for component tree subscriptions)**
25
+ *
26
+ * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
27
+ * needing to pass a selector to the hook. This is ideal when you want to subscribe to state
28
+ * in child components.
29
+ *
30
+ * **2. Using the `selector` parameter (For hook-level subscriptions)**
31
+ *
32
+ * The `selector` parameter allows you to specify which state changes will trigger a re-render
33
+ * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
34
+ * state changes occur.
24
35
  *
25
36
  * **By default, there will be no reactive state subscriptions** and you must opt-in to state
26
- * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
27
- * full control over when your component updates. Only when you provide a selector will the
28
- * component re-render when the selected state values change.
37
+ * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
38
+ * re-renders and gives you full control over when your component updates.
29
39
  *
30
40
  * Available state properties:
31
41
  * - `executionCount`: Number of batch executions that have been completed
@@ -45,7 +55,14 @@ let __tanstack_pacer_batcher = require("@tanstack/pacer/batcher");
45
55
  * { maxSize: 5, wait: 2000 }
46
56
  * );
47
57
  *
48
- * // Opt-in to re-render when batch size changes (optimized for displaying queue size)
58
+ * // Subscribe to state changes deep in component tree using Subscribe HOC
59
+ * <batcher.Subscribe selector={(state) => ({ size: state.size })}>
60
+ * {({ size }) => (
61
+ * <div>Batch Size: {size}</div>
62
+ * )}
63
+ * </batcher.Subscribe>
64
+ *
65
+ * // Opt-in to re-render when batch size changes at hook level (optimized for displaying queue size)
49
66
  * const batcher = useBatcher<number>(
50
67
  * (items) => console.log('Processing batch:', items),
51
68
  * { maxSize: 5, wait: 2000 },
@@ -107,10 +124,17 @@ function useBatcher(fn, options = {}, selector = () => ({})) {
107
124
  ...require_PacerProvider.useDefaultPacerOptions().batcher,
108
125
  ...options
109
126
  };
110
- const [batcher] = (0, preact_hooks.useState)(() => new __tanstack_pacer_batcher.Batcher(fn, mergedOptions));
127
+ const [batcher] = (0, preact_hooks.useState)(() => {
128
+ const batcherInstance = new _tanstack_pacer_batcher.Batcher(fn, mergedOptions);
129
+ batcherInstance.Subscribe = function Subscribe(props) {
130
+ const selected = (0, _tanstack_preact_store.useStore)(batcherInstance.store, props.selector);
131
+ return typeof props.children === "function" ? props.children(selected) : props.children;
132
+ };
133
+ return batcherInstance;
134
+ });
111
135
  batcher.fn = fn;
112
136
  batcher.setOptions(mergedOptions);
113
- const state = (0, __tanstack_preact_store.useStore)(batcher.store, selector);
137
+ const state = (0, _tanstack_preact_store.useStore)(batcher.store, selector);
114
138
  return (0, preact_hooks.useMemo)(() => ({
115
139
  ...batcher,
116
140
  state
@@ -1 +1 @@
1
- {"version":3,"file":"useBatcher.cjs","names":["useDefaultPacerOptions","Batcher"],"sources":["../../src/batcher/useBatcher.ts"],"sourcesContent":["import { useMemo, useState } from 'preact/hooks'\nimport { Batcher } from '@tanstack/pacer/batcher'\nimport { useStore } from '@tanstack/preact-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/preact-store'\nimport type { BatcherOptions, BatcherState } from '@tanstack/pacer/batcher'\n\nexport interface PreactBatcher<TValue, TSelected = {}> extends Omit<\n Batcher<TValue>,\n 'store'\n> {\n /**\n * Reactive state that will be updated and re-rendered when the batcher state changes\n *\n * Use this instead of `batcher.store.state`\n */\n readonly state: Readonly<TSelected>\n /**\n * @deprecated Use `batcher.state` instead of `batcher.store.state` if you want to read reactive state.\n * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.\n * Although, you can make the state reactive by using the `useStore` in your own usage.\n */\n readonly store: Store<Readonly<BatcherState<TValue>>>\n}\n\n/**\n * A Preact hook that creates and manages a Batcher instance.\n *\n * This is a lower-level hook that provides direct access to the Batcher's functionality without\n * any built-in state management. This allows you to integrate it with any state management solution\n * you prefer (useState, Redux, Zustand, etc.) by utilizing the onItemsChange callback.\n *\n * The Batcher collects items and processes them in batches based on configurable conditions:\n * - Maximum batch size\n * - Time-based batching (process after X milliseconds)\n * - Custom batch processing logic via getShouldExecute\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you\n * to specify which state changes will trigger a re-render, optimizing performance by preventing\n * unnecessary re-renders when irrelevant state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function. This prevents unnecessary re-renders and gives you\n * full control over when your component updates. Only when you provide a selector will the\n * component re-render when the selected state values change.\n *\n * Available state properties:\n * - `executionCount`: Number of batch executions that have been completed\n * - `isEmpty`: Whether the batcher has no items to process\n * - `isPending`: Whether the batcher is waiting for the timeout to trigger batch processing\n * - `isRunning`: Whether the batcher is active and will process items automatically\n * - `items`: Array of items currently queued for batch processing\n * - `size`: Number of items currently in the batch queue\n * - `status`: Current processing status ('idle' | 'pending')\n * - `totalItemsProcessed`: Total number of items processed across all batches\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 }\n * );\n *\n * // Opt-in to re-render when batch size changes (optimized for displaying queue size)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * size: state.size,\n * isEmpty: state.isEmpty\n * })\n * );\n *\n * // Opt-in to re-render when execution metrics change (optimized for stats display)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * executionCount: state.executionCount,\n * totalItemsProcessed: state.totalItemsProcessed\n * })\n * );\n *\n * // Opt-in to re-render when processing state changes (optimized for loading indicators)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * isPending: state.isPending,\n * isRunning: state.isRunning,\n * status: state.status\n * })\n * );\n *\n * // Example with custom state management and batching\n * const [items, setItems] = useState([]);\n *\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * {\n * maxSize: 5,\n * wait: 2000,\n * onItemsChange: (batcher) => setItems(batcher.peekAllItems()),\n * getShouldExecute: (items) => items.length >= 3\n * }\n * );\n *\n * // Add items to batch - they'll be processed when conditions are met\n * batcher.addItem(1);\n * batcher.addItem(2);\n * batcher.addItem(3); // Triggers batch processing\n *\n * // Control the batcher\n * batcher.stop(); // Pause batching\n * batcher.start(); // Resume batching\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { size, isPending } = batcher.state;\n * ```\n */\nexport function useBatcher<TValue, TSelected = {}>(\n fn: (items: Array<TValue>) => void,\n options: BatcherOptions<TValue> = {},\n selector: (state: BatcherState<TValue>) => TSelected = () =>\n ({}) as TSelected,\n): PreactBatcher<TValue, TSelected> {\n const mergedOptions = {\n ...useDefaultPacerOptions().batcher,\n ...options,\n } as BatcherOptions<TValue>\n\n const [batcher] = useState(() => new Batcher<TValue>(fn, mergedOptions))\n\n batcher.fn = fn\n batcher.setOptions(mergedOptions)\n\n const state = useStore(batcher.store, selector)\n\n return useMemo(\n () =>\n ({\n ...batcher,\n state,\n }) as PreactBatcher<TValue, TSelected>, // omit `store` in favor of `state`\n [batcher, state],\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2HA,SAAgB,WACd,IACA,UAAkC,EAAE,EACpC,kBACG,EAAE,GAC6B;CAClC,MAAM,gBAAgB;EACpB,GAAGA,8CAAwB,CAAC;EAC5B,GAAG;EACJ;CAED,MAAM,CAAC,4CAA0B,IAAIC,iCAAgB,IAAI,cAAc,CAAC;AAExE,SAAQ,KAAK;AACb,SAAQ,WAAW,cAAc;CAEjC,MAAM,8CAAiB,QAAQ,OAAO,SAAS;AAE/C,yCAEK;EACC,GAAG;EACH;EACD,GACH,CAAC,SAAS,MAAM,CACjB"}
1
+ {"version":3,"file":"useBatcher.cjs","names":["useDefaultPacerOptions","Batcher"],"sources":["../../src/batcher/useBatcher.ts"],"sourcesContent":["import { useMemo, useState } from 'preact/hooks'\nimport { Batcher } from '@tanstack/pacer/batcher'\nimport { useStore } from '@tanstack/preact-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/preact-store'\nimport type { BatcherOptions, BatcherState } from '@tanstack/pacer/batcher'\nimport type { ComponentChildren } from 'preact'\n\nexport interface PreactBatcher<TValue, TSelected = {}> extends Omit<\n Batcher<TValue>,\n 'store'\n> {\n /**\n * A Preact HOC (Higher Order Component) that allows you to subscribe to the batcher state.\n *\n * This is useful for opting into state re-renders for specific parts of the batcher state\n * deep in your component tree without needing to pass a selector to the hook.\n *\n * @example\n * <batcher.Subscribe selector={(state) => ({ size: state.size })}>\n * {({ size }) => (\n * <div>Batch Size: {size}</div>\n * )}\n * </batcher.Subscribe>\n */\n Subscribe: <TSelected>(props: {\n selector: (state: BatcherState<TValue>) => TSelected\n children: ((state: TSelected) => ComponentChildren) | ComponentChildren\n }) => ComponentChildren\n /**\n * Reactive state that will be updated and re-rendered when the batcher state changes\n *\n * Use this instead of `batcher.store.state`\n */\n readonly state: Readonly<TSelected>\n /**\n * @deprecated Use `batcher.state` instead of `batcher.store.state` if you want to read reactive state.\n * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.\n * Although, you can make the state reactive by using the `useStore` in your own usage.\n */\n readonly store: Store<Readonly<BatcherState<TValue>>>\n}\n\n/**\n * A Preact hook that creates and manages a Batcher instance.\n *\n * This is a lower-level hook that provides direct access to the Batcher's functionality without\n * any built-in state management. This allows you to integrate it with any state management solution\n * you prefer (useState, Redux, Zustand, etc.) by utilizing the onItemsChange callback.\n *\n * The Batcher collects items and processes them in batches based on configurable conditions:\n * - Maximum batch size\n * - Time-based batching (process after X milliseconds)\n * - Custom batch processing logic via getShouldExecute\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. You can subscribe to state changes\n * in two ways:\n *\n * **1. Using `batcher.Subscribe` HOC (Recommended for component tree subscriptions)**\n *\n * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without\n * needing to pass a selector to the hook. This is ideal when you want to subscribe to state\n * in child components.\n *\n * **2. Using the `selector` parameter (For hook-level subscriptions)**\n *\n * The `selector` parameter allows you to specify which state changes will trigger a re-render\n * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant\n * state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary\n * re-renders and gives you full control over when your component updates.\n *\n * Available state properties:\n * - `executionCount`: Number of batch executions that have been completed\n * - `isEmpty`: Whether the batcher has no items to process\n * - `isPending`: Whether the batcher is waiting for the timeout to trigger batch processing\n * - `isRunning`: Whether the batcher is active and will process items automatically\n * - `items`: Array of items currently queued for batch processing\n * - `size`: Number of items currently in the batch queue\n * - `status`: Current processing status ('idle' | 'pending')\n * - `totalItemsProcessed`: Total number of items processed across all batches\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 }\n * );\n *\n * // Subscribe to state changes deep in component tree using Subscribe HOC\n * <batcher.Subscribe selector={(state) => ({ size: state.size })}>\n * {({ size }) => (\n * <div>Batch Size: {size}</div>\n * )}\n * </batcher.Subscribe>\n *\n * // Opt-in to re-render when batch size changes at hook level (optimized for displaying queue size)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * size: state.size,\n * isEmpty: state.isEmpty\n * })\n * );\n *\n * // Opt-in to re-render when execution metrics change (optimized for stats display)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * executionCount: state.executionCount,\n * totalItemsProcessed: state.totalItemsProcessed\n * })\n * );\n *\n * // Opt-in to re-render when processing state changes (optimized for loading indicators)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * isPending: state.isPending,\n * isRunning: state.isRunning,\n * status: state.status\n * })\n * );\n *\n * // Example with custom state management and batching\n * const [items, setItems] = useState([]);\n *\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * {\n * maxSize: 5,\n * wait: 2000,\n * onItemsChange: (batcher) => setItems(batcher.peekAllItems()),\n * getShouldExecute: (items) => items.length >= 3\n * }\n * );\n *\n * // Add items to batch - they'll be processed when conditions are met\n * batcher.addItem(1);\n * batcher.addItem(2);\n * batcher.addItem(3); // Triggers batch processing\n *\n * // Control the batcher\n * batcher.stop(); // Pause batching\n * batcher.start(); // Resume batching\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { size, isPending } = batcher.state;\n * ```\n */\nexport function useBatcher<TValue, TSelected = {}>(\n fn: (items: Array<TValue>) => void,\n options: BatcherOptions<TValue> = {},\n selector: (state: BatcherState<TValue>) => TSelected = () =>\n ({}) as TSelected,\n): PreactBatcher<TValue, TSelected> {\n const mergedOptions = {\n ...useDefaultPacerOptions().batcher,\n ...options,\n } as BatcherOptions<TValue>\n\n const [batcher] = useState(() => {\n const batcherInstance = new Batcher<TValue>(\n fn,\n mergedOptions,\n ) as unknown as PreactBatcher<TValue, TSelected>\n\n batcherInstance.Subscribe = function Subscribe<TSelected>(props: {\n selector: (state: BatcherState<TValue>) => TSelected\n children: ((state: TSelected) => ComponentChildren) | ComponentChildren\n }) {\n const selected = useStore(batcherInstance.store, props.selector)\n\n return typeof props.children === 'function'\n ? props.children(selected)\n : props.children\n }\n\n return batcherInstance\n })\n\n batcher.fn = fn\n batcher.setOptions(mergedOptions)\n\n const state = useStore(batcher.store, selector)\n\n return useMemo(\n () =>\n ({\n ...batcher,\n state,\n }) as PreactBatcher<TValue, TSelected>, // omit `store` in favor of `state`\n [batcher, state],\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8JA,SAAgB,WACd,IACA,UAAkC,EAAE,EACpC,kBACG,EAAE,GAC6B;CAClC,MAAM,gBAAgB;EACpB,GAAGA,8CAAwB,CAAC;EAC5B,GAAG;EACJ;CAED,MAAM,CAAC,4CAA0B;EAC/B,MAAM,kBAAkB,IAAIC,gCAC1B,IACA,cACD;AAED,kBAAgB,YAAY,SAAS,UAAqB,OAGvD;GACD,MAAM,gDAAoB,gBAAgB,OAAO,MAAM,SAAS;AAEhE,UAAO,OAAO,MAAM,aAAa,aAC7B,MAAM,SAAS,SAAS,GACxB,MAAM;;AAGZ,SAAO;GACP;AAEF,SAAQ,KAAK;AACb,SAAQ,WAAW,cAAc;CAEjC,MAAM,6CAAiB,QAAQ,OAAO,SAAS;AAE/C,yCAEK;EACC,GAAG;EACH;EACD,GACH,CAAC,SAAS,MAAM,CACjB"}
@@ -1,8 +1,26 @@
1
1
  import { Store } from "@tanstack/preact-store";
2
+ import { ComponentChildren } from "preact";
2
3
  import { Batcher, BatcherOptions, BatcherState } from "@tanstack/pacer/batcher";
3
4
 
4
5
  //#region src/batcher/useBatcher.d.ts
5
6
  interface PreactBatcher<TValue, TSelected = {}> extends Omit<Batcher<TValue>, 'store'> {
7
+ /**
8
+ * A Preact HOC (Higher Order Component) that allows you to subscribe to the batcher state.
9
+ *
10
+ * This is useful for opting into state re-renders for specific parts of the batcher state
11
+ * deep in your component tree without needing to pass a selector to the hook.
12
+ *
13
+ * @example
14
+ * <batcher.Subscribe selector={(state) => ({ size: state.size })}>
15
+ * {({ size }) => (
16
+ * <div>Batch Size: {size}</div>
17
+ * )}
18
+ * </batcher.Subscribe>
19
+ */
20
+ Subscribe: <TSelected>(props: {
21
+ selector: (state: BatcherState<TValue>) => TSelected;
22
+ children: ((state: TSelected) => ComponentChildren) | ComponentChildren;
23
+ }) => ComponentChildren;
6
24
  /**
7
25
  * Reactive state that will be updated and re-rendered when the batcher state changes
8
26
  *
@@ -30,14 +48,24 @@ interface PreactBatcher<TValue, TSelected = {}> extends Omit<Batcher<TValue>, 's
30
48
  *
31
49
  * ## State Management and Selector
32
50
  *
33
- * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
34
- * to specify which state changes will trigger a re-render, optimizing performance by preventing
35
- * unnecessary re-renders when irrelevant state changes occur.
51
+ * The hook uses TanStack Store for reactive state management. You can subscribe to state changes
52
+ * in two ways:
53
+ *
54
+ * **1. Using `batcher.Subscribe` HOC (Recommended for component tree subscriptions)**
55
+ *
56
+ * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
57
+ * needing to pass a selector to the hook. This is ideal when you want to subscribe to state
58
+ * in child components.
59
+ *
60
+ * **2. Using the `selector` parameter (For hook-level subscriptions)**
61
+ *
62
+ * The `selector` parameter allows you to specify which state changes will trigger a re-render
63
+ * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
64
+ * state changes occur.
36
65
  *
37
66
  * **By default, there will be no reactive state subscriptions** and you must opt-in to state
38
- * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
39
- * full control over when your component updates. Only when you provide a selector will the
40
- * component re-render when the selected state values change.
67
+ * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
68
+ * re-renders and gives you full control over when your component updates.
41
69
  *
42
70
  * Available state properties:
43
71
  * - `executionCount`: Number of batch executions that have been completed
@@ -57,7 +85,14 @@ interface PreactBatcher<TValue, TSelected = {}> extends Omit<Batcher<TValue>, 's
57
85
  * { maxSize: 5, wait: 2000 }
58
86
  * );
59
87
  *
60
- * // Opt-in to re-render when batch size changes (optimized for displaying queue size)
88
+ * // Subscribe to state changes deep in component tree using Subscribe HOC
89
+ * <batcher.Subscribe selector={(state) => ({ size: state.size })}>
90
+ * {({ size }) => (
91
+ * <div>Batch Size: {size}</div>
92
+ * )}
93
+ * </batcher.Subscribe>
94
+ *
95
+ * // Opt-in to re-render when batch size changes at hook level (optimized for displaying queue size)
61
96
  * const batcher = useBatcher<number>(
62
97
  * (items) => console.log('Processing batch:', items),
63
98
  * { maxSize: 5, wait: 2000 },
@@ -1,8 +1,26 @@
1
+ import { ComponentChildren } from "preact";
1
2
  import { Store } from "@tanstack/preact-store";
2
3
  import { Batcher, BatcherOptions, BatcherState } from "@tanstack/pacer/batcher";
3
4
 
4
5
  //#region src/batcher/useBatcher.d.ts
5
6
  interface PreactBatcher<TValue, TSelected = {}> extends Omit<Batcher<TValue>, 'store'> {
7
+ /**
8
+ * A Preact HOC (Higher Order Component) that allows you to subscribe to the batcher state.
9
+ *
10
+ * This is useful for opting into state re-renders for specific parts of the batcher state
11
+ * deep in your component tree without needing to pass a selector to the hook.
12
+ *
13
+ * @example
14
+ * <batcher.Subscribe selector={(state) => ({ size: state.size })}>
15
+ * {({ size }) => (
16
+ * <div>Batch Size: {size}</div>
17
+ * )}
18
+ * </batcher.Subscribe>
19
+ */
20
+ Subscribe: <TSelected>(props: {
21
+ selector: (state: BatcherState<TValue>) => TSelected;
22
+ children: ((state: TSelected) => ComponentChildren) | ComponentChildren;
23
+ }) => ComponentChildren;
6
24
  /**
7
25
  * Reactive state that will be updated and re-rendered when the batcher state changes
8
26
  *
@@ -30,14 +48,24 @@ interface PreactBatcher<TValue, TSelected = {}> extends Omit<Batcher<TValue>, 's
30
48
  *
31
49
  * ## State Management and Selector
32
50
  *
33
- * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
34
- * to specify which state changes will trigger a re-render, optimizing performance by preventing
35
- * unnecessary re-renders when irrelevant state changes occur.
51
+ * The hook uses TanStack Store for reactive state management. You can subscribe to state changes
52
+ * in two ways:
53
+ *
54
+ * **1. Using `batcher.Subscribe` HOC (Recommended for component tree subscriptions)**
55
+ *
56
+ * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
57
+ * needing to pass a selector to the hook. This is ideal when you want to subscribe to state
58
+ * in child components.
59
+ *
60
+ * **2. Using the `selector` parameter (For hook-level subscriptions)**
61
+ *
62
+ * The `selector` parameter allows you to specify which state changes will trigger a re-render
63
+ * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
64
+ * state changes occur.
36
65
  *
37
66
  * **By default, there will be no reactive state subscriptions** and you must opt-in to state
38
- * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
39
- * full control over when your component updates. Only when you provide a selector will the
40
- * component re-render when the selected state values change.
67
+ * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
68
+ * re-renders and gives you full control over when your component updates.
41
69
  *
42
70
  * Available state properties:
43
71
  * - `executionCount`: Number of batch executions that have been completed
@@ -57,7 +85,14 @@ interface PreactBatcher<TValue, TSelected = {}> extends Omit<Batcher<TValue>, 's
57
85
  * { maxSize: 5, wait: 2000 }
58
86
  * );
59
87
  *
60
- * // Opt-in to re-render when batch size changes (optimized for displaying queue size)
88
+ * // Subscribe to state changes deep in component tree using Subscribe HOC
89
+ * <batcher.Subscribe selector={(state) => ({ size: state.size })}>
90
+ * {({ size }) => (
91
+ * <div>Batch Size: {size}</div>
92
+ * )}
93
+ * </batcher.Subscribe>
94
+ *
95
+ * // Opt-in to re-render when batch size changes at hook level (optimized for displaying queue size)
61
96
  * const batcher = useBatcher<number>(
62
97
  * (items) => console.log('Processing batch:', items),
63
98
  * { maxSize: 5, wait: 2000 },
@@ -18,14 +18,24 @@ import { Batcher } from "@tanstack/pacer/batcher";
18
18
  *
19
19
  * ## State Management and Selector
20
20
  *
21
- * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
22
- * to specify which state changes will trigger a re-render, optimizing performance by preventing
23
- * unnecessary re-renders when irrelevant state changes occur.
21
+ * The hook uses TanStack Store for reactive state management. You can subscribe to state changes
22
+ * in two ways:
23
+ *
24
+ * **1. Using `batcher.Subscribe` HOC (Recommended for component tree subscriptions)**
25
+ *
26
+ * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
27
+ * needing to pass a selector to the hook. This is ideal when you want to subscribe to state
28
+ * in child components.
29
+ *
30
+ * **2. Using the `selector` parameter (For hook-level subscriptions)**
31
+ *
32
+ * The `selector` parameter allows you to specify which state changes will trigger a re-render
33
+ * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
34
+ * state changes occur.
24
35
  *
25
36
  * **By default, there will be no reactive state subscriptions** and you must opt-in to state
26
- * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
27
- * full control over when your component updates. Only when you provide a selector will the
28
- * component re-render when the selected state values change.
37
+ * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
38
+ * re-renders and gives you full control over when your component updates.
29
39
  *
30
40
  * Available state properties:
31
41
  * - `executionCount`: Number of batch executions that have been completed
@@ -45,7 +55,14 @@ import { Batcher } from "@tanstack/pacer/batcher";
45
55
  * { maxSize: 5, wait: 2000 }
46
56
  * );
47
57
  *
48
- * // Opt-in to re-render when batch size changes (optimized for displaying queue size)
58
+ * // Subscribe to state changes deep in component tree using Subscribe HOC
59
+ * <batcher.Subscribe selector={(state) => ({ size: state.size })}>
60
+ * {({ size }) => (
61
+ * <div>Batch Size: {size}</div>
62
+ * )}
63
+ * </batcher.Subscribe>
64
+ *
65
+ * // Opt-in to re-render when batch size changes at hook level (optimized for displaying queue size)
49
66
  * const batcher = useBatcher<number>(
50
67
  * (items) => console.log('Processing batch:', items),
51
68
  * { maxSize: 5, wait: 2000 },
@@ -107,7 +124,14 @@ function useBatcher(fn, options = {}, selector = () => ({})) {
107
124
  ...useDefaultPacerOptions().batcher,
108
125
  ...options
109
126
  };
110
- const [batcher] = useState(() => new Batcher(fn, mergedOptions));
127
+ const [batcher] = useState(() => {
128
+ const batcherInstance = new Batcher(fn, mergedOptions);
129
+ batcherInstance.Subscribe = function Subscribe(props) {
130
+ const selected = useStore(batcherInstance.store, props.selector);
131
+ return typeof props.children === "function" ? props.children(selected) : props.children;
132
+ };
133
+ return batcherInstance;
134
+ });
111
135
  batcher.fn = fn;
112
136
  batcher.setOptions(mergedOptions);
113
137
  const state = useStore(batcher.store, selector);
@@ -1 +1 @@
1
- {"version":3,"file":"useBatcher.js","names":[],"sources":["../../src/batcher/useBatcher.ts"],"sourcesContent":["import { useMemo, useState } from 'preact/hooks'\nimport { Batcher } from '@tanstack/pacer/batcher'\nimport { useStore } from '@tanstack/preact-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/preact-store'\nimport type { BatcherOptions, BatcherState } from '@tanstack/pacer/batcher'\n\nexport interface PreactBatcher<TValue, TSelected = {}> extends Omit<\n Batcher<TValue>,\n 'store'\n> {\n /**\n * Reactive state that will be updated and re-rendered when the batcher state changes\n *\n * Use this instead of `batcher.store.state`\n */\n readonly state: Readonly<TSelected>\n /**\n * @deprecated Use `batcher.state` instead of `batcher.store.state` if you want to read reactive state.\n * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.\n * Although, you can make the state reactive by using the `useStore` in your own usage.\n */\n readonly store: Store<Readonly<BatcherState<TValue>>>\n}\n\n/**\n * A Preact hook that creates and manages a Batcher instance.\n *\n * This is a lower-level hook that provides direct access to the Batcher's functionality without\n * any built-in state management. This allows you to integrate it with any state management solution\n * you prefer (useState, Redux, Zustand, etc.) by utilizing the onItemsChange callback.\n *\n * The Batcher collects items and processes them in batches based on configurable conditions:\n * - Maximum batch size\n * - Time-based batching (process after X milliseconds)\n * - Custom batch processing logic via getShouldExecute\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you\n * to specify which state changes will trigger a re-render, optimizing performance by preventing\n * unnecessary re-renders when irrelevant state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function. This prevents unnecessary re-renders and gives you\n * full control over when your component updates. Only when you provide a selector will the\n * component re-render when the selected state values change.\n *\n * Available state properties:\n * - `executionCount`: Number of batch executions that have been completed\n * - `isEmpty`: Whether the batcher has no items to process\n * - `isPending`: Whether the batcher is waiting for the timeout to trigger batch processing\n * - `isRunning`: Whether the batcher is active and will process items automatically\n * - `items`: Array of items currently queued for batch processing\n * - `size`: Number of items currently in the batch queue\n * - `status`: Current processing status ('idle' | 'pending')\n * - `totalItemsProcessed`: Total number of items processed across all batches\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 }\n * );\n *\n * // Opt-in to re-render when batch size changes (optimized for displaying queue size)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * size: state.size,\n * isEmpty: state.isEmpty\n * })\n * );\n *\n * // Opt-in to re-render when execution metrics change (optimized for stats display)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * executionCount: state.executionCount,\n * totalItemsProcessed: state.totalItemsProcessed\n * })\n * );\n *\n * // Opt-in to re-render when processing state changes (optimized for loading indicators)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * isPending: state.isPending,\n * isRunning: state.isRunning,\n * status: state.status\n * })\n * );\n *\n * // Example with custom state management and batching\n * const [items, setItems] = useState([]);\n *\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * {\n * maxSize: 5,\n * wait: 2000,\n * onItemsChange: (batcher) => setItems(batcher.peekAllItems()),\n * getShouldExecute: (items) => items.length >= 3\n * }\n * );\n *\n * // Add items to batch - they'll be processed when conditions are met\n * batcher.addItem(1);\n * batcher.addItem(2);\n * batcher.addItem(3); // Triggers batch processing\n *\n * // Control the batcher\n * batcher.stop(); // Pause batching\n * batcher.start(); // Resume batching\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { size, isPending } = batcher.state;\n * ```\n */\nexport function useBatcher<TValue, TSelected = {}>(\n fn: (items: Array<TValue>) => void,\n options: BatcherOptions<TValue> = {},\n selector: (state: BatcherState<TValue>) => TSelected = () =>\n ({}) as TSelected,\n): PreactBatcher<TValue, TSelected> {\n const mergedOptions = {\n ...useDefaultPacerOptions().batcher,\n ...options,\n } as BatcherOptions<TValue>\n\n const [batcher] = useState(() => new Batcher<TValue>(fn, mergedOptions))\n\n batcher.fn = fn\n batcher.setOptions(mergedOptions)\n\n const state = useStore(batcher.store, selector)\n\n return useMemo(\n () =>\n ({\n ...batcher,\n state,\n }) as PreactBatcher<TValue, TSelected>, // omit `store` in favor of `state`\n [batcher, state],\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2HA,SAAgB,WACd,IACA,UAAkC,EAAE,EACpC,kBACG,EAAE,GAC6B;CAClC,MAAM,gBAAgB;EACpB,GAAG,wBAAwB,CAAC;EAC5B,GAAG;EACJ;CAED,MAAM,CAAC,WAAW,eAAe,IAAI,QAAgB,IAAI,cAAc,CAAC;AAExE,SAAQ,KAAK;AACb,SAAQ,WAAW,cAAc;CAEjC,MAAM,QAAQ,SAAS,QAAQ,OAAO,SAAS;AAE/C,QAAO,eAEF;EACC,GAAG;EACH;EACD,GACH,CAAC,SAAS,MAAM,CACjB"}
1
+ {"version":3,"file":"useBatcher.js","names":[],"sources":["../../src/batcher/useBatcher.ts"],"sourcesContent":["import { useMemo, useState } from 'preact/hooks'\nimport { Batcher } from '@tanstack/pacer/batcher'\nimport { useStore } from '@tanstack/preact-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/preact-store'\nimport type { BatcherOptions, BatcherState } from '@tanstack/pacer/batcher'\nimport type { ComponentChildren } from 'preact'\n\nexport interface PreactBatcher<TValue, TSelected = {}> extends Omit<\n Batcher<TValue>,\n 'store'\n> {\n /**\n * A Preact HOC (Higher Order Component) that allows you to subscribe to the batcher state.\n *\n * This is useful for opting into state re-renders for specific parts of the batcher state\n * deep in your component tree without needing to pass a selector to the hook.\n *\n * @example\n * <batcher.Subscribe selector={(state) => ({ size: state.size })}>\n * {({ size }) => (\n * <div>Batch Size: {size}</div>\n * )}\n * </batcher.Subscribe>\n */\n Subscribe: <TSelected>(props: {\n selector: (state: BatcherState<TValue>) => TSelected\n children: ((state: TSelected) => ComponentChildren) | ComponentChildren\n }) => ComponentChildren\n /**\n * Reactive state that will be updated and re-rendered when the batcher state changes\n *\n * Use this instead of `batcher.store.state`\n */\n readonly state: Readonly<TSelected>\n /**\n * @deprecated Use `batcher.state` instead of `batcher.store.state` if you want to read reactive state.\n * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.\n * Although, you can make the state reactive by using the `useStore` in your own usage.\n */\n readonly store: Store<Readonly<BatcherState<TValue>>>\n}\n\n/**\n * A Preact hook that creates and manages a Batcher instance.\n *\n * This is a lower-level hook that provides direct access to the Batcher's functionality without\n * any built-in state management. This allows you to integrate it with any state management solution\n * you prefer (useState, Redux, Zustand, etc.) by utilizing the onItemsChange callback.\n *\n * The Batcher collects items and processes them in batches based on configurable conditions:\n * - Maximum batch size\n * - Time-based batching (process after X milliseconds)\n * - Custom batch processing logic via getShouldExecute\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. You can subscribe to state changes\n * in two ways:\n *\n * **1. Using `batcher.Subscribe` HOC (Recommended for component tree subscriptions)**\n *\n * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without\n * needing to pass a selector to the hook. This is ideal when you want to subscribe to state\n * in child components.\n *\n * **2. Using the `selector` parameter (For hook-level subscriptions)**\n *\n * The `selector` parameter allows you to specify which state changes will trigger a re-render\n * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant\n * state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary\n * re-renders and gives you full control over when your component updates.\n *\n * Available state properties:\n * - `executionCount`: Number of batch executions that have been completed\n * - `isEmpty`: Whether the batcher has no items to process\n * - `isPending`: Whether the batcher is waiting for the timeout to trigger batch processing\n * - `isRunning`: Whether the batcher is active and will process items automatically\n * - `items`: Array of items currently queued for batch processing\n * - `size`: Number of items currently in the batch queue\n * - `status`: Current processing status ('idle' | 'pending')\n * - `totalItemsProcessed`: Total number of items processed across all batches\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 }\n * );\n *\n * // Subscribe to state changes deep in component tree using Subscribe HOC\n * <batcher.Subscribe selector={(state) => ({ size: state.size })}>\n * {({ size }) => (\n * <div>Batch Size: {size}</div>\n * )}\n * </batcher.Subscribe>\n *\n * // Opt-in to re-render when batch size changes at hook level (optimized for displaying queue size)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * size: state.size,\n * isEmpty: state.isEmpty\n * })\n * );\n *\n * // Opt-in to re-render when execution metrics change (optimized for stats display)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * executionCount: state.executionCount,\n * totalItemsProcessed: state.totalItemsProcessed\n * })\n * );\n *\n * // Opt-in to re-render when processing state changes (optimized for loading indicators)\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * { maxSize: 5, wait: 2000 },\n * (state) => ({\n * isPending: state.isPending,\n * isRunning: state.isRunning,\n * status: state.status\n * })\n * );\n *\n * // Example with custom state management and batching\n * const [items, setItems] = useState([]);\n *\n * const batcher = useBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * {\n * maxSize: 5,\n * wait: 2000,\n * onItemsChange: (batcher) => setItems(batcher.peekAllItems()),\n * getShouldExecute: (items) => items.length >= 3\n * }\n * );\n *\n * // Add items to batch - they'll be processed when conditions are met\n * batcher.addItem(1);\n * batcher.addItem(2);\n * batcher.addItem(3); // Triggers batch processing\n *\n * // Control the batcher\n * batcher.stop(); // Pause batching\n * batcher.start(); // Resume batching\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { size, isPending } = batcher.state;\n * ```\n */\nexport function useBatcher<TValue, TSelected = {}>(\n fn: (items: Array<TValue>) => void,\n options: BatcherOptions<TValue> = {},\n selector: (state: BatcherState<TValue>) => TSelected = () =>\n ({}) as TSelected,\n): PreactBatcher<TValue, TSelected> {\n const mergedOptions = {\n ...useDefaultPacerOptions().batcher,\n ...options,\n } as BatcherOptions<TValue>\n\n const [batcher] = useState(() => {\n const batcherInstance = new Batcher<TValue>(\n fn,\n mergedOptions,\n ) as unknown as PreactBatcher<TValue, TSelected>\n\n batcherInstance.Subscribe = function Subscribe<TSelected>(props: {\n selector: (state: BatcherState<TValue>) => TSelected\n children: ((state: TSelected) => ComponentChildren) | ComponentChildren\n }) {\n const selected = useStore(batcherInstance.store, props.selector)\n\n return typeof props.children === 'function'\n ? props.children(selected)\n : props.children\n }\n\n return batcherInstance\n })\n\n batcher.fn = fn\n batcher.setOptions(mergedOptions)\n\n const state = useStore(batcher.store, selector)\n\n return useMemo(\n () =>\n ({\n ...batcher,\n state,\n }) as PreactBatcher<TValue, TSelected>, // omit `store` in favor of `state`\n [batcher, state],\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8JA,SAAgB,WACd,IACA,UAAkC,EAAE,EACpC,kBACG,EAAE,GAC6B;CAClC,MAAM,gBAAgB;EACpB,GAAG,wBAAwB,CAAC;EAC5B,GAAG;EACJ;CAED,MAAM,CAAC,WAAW,eAAe;EAC/B,MAAM,kBAAkB,IAAI,QAC1B,IACA,cACD;AAED,kBAAgB,YAAY,SAAS,UAAqB,OAGvD;GACD,MAAM,WAAW,SAAS,gBAAgB,OAAO,MAAM,SAAS;AAEhE,UAAO,OAAO,MAAM,aAAa,aAC7B,MAAM,SAAS,SAAS,GACxB,MAAM;;AAGZ,SAAO;GACP;AAEF,SAAQ,KAAK;AACb,SAAQ,WAAW,cAAc;CAEjC,MAAM,QAAQ,SAAS,QAAQ,OAAO,SAAS;AAE/C,QAAO,eAEF;EACC,GAAG;EACH;EACD,GACH,CAAC,SAAS,MAAM,CACjB"}
@@ -7,10 +7,10 @@ exports.useDebouncedCallback = require_useDebouncedCallback.useDebouncedCallback
7
7
  exports.useDebouncedState = require_useDebouncedState.useDebouncedState;
8
8
  exports.useDebouncedValue = require_useDebouncedValue.useDebouncedValue;
9
9
  exports.useDebouncer = require_useDebouncer.useDebouncer;
10
- var __tanstack_pacer_debouncer = require("@tanstack/pacer/debouncer");
11
- Object.keys(__tanstack_pacer_debouncer).forEach(function (k) {
10
+ var _tanstack_pacer_debouncer = require("@tanstack/pacer/debouncer");
11
+ Object.keys(_tanstack_pacer_debouncer).forEach(function (k) {
12
12
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
13
13
  enumerable: true,
14
- get: function () { return __tanstack_pacer_debouncer[k]; }
14
+ get: function () { return _tanstack_pacer_debouncer[k]; }
15
15
  });
16
16
  });
@@ -1,7 +1,7 @@
1
1
  const require_PacerProvider = require('../provider/PacerProvider.cjs');
2
2
  let preact_hooks = require("preact/hooks");
3
- let __tanstack_preact_store = require("@tanstack/preact-store");
4
- let __tanstack_pacer_debouncer = require("@tanstack/pacer/debouncer");
3
+ let _tanstack_preact_store = require("@tanstack/preact-store");
4
+ let _tanstack_pacer_debouncer = require("@tanstack/pacer/debouncer");
5
5
 
6
6
  //#region src/debouncer/useDebouncer.ts
7
7
  /**
@@ -21,14 +21,24 @@ let __tanstack_pacer_debouncer = require("@tanstack/pacer/debouncer");
21
21
  *
22
22
  * ## State Management and Selector
23
23
  *
24
- * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
25
- * to specify which state changes will trigger a re-render, optimizing performance by preventing
26
- * unnecessary re-renders when irrelevant state changes occur.
24
+ * The hook uses TanStack Store for reactive state management. You can subscribe to state changes
25
+ * in two ways:
26
+ *
27
+ * **1. Using `debouncer.Subscribe` HOC (Recommended for component tree subscriptions)**
28
+ *
29
+ * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
30
+ * needing to pass a selector to the hook. This is ideal when you want to subscribe to state
31
+ * in child components.
32
+ *
33
+ * **2. Using the `selector` parameter (For hook-level subscriptions)**
34
+ *
35
+ * The `selector` parameter allows you to specify which state changes will trigger a re-render
36
+ * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
37
+ * state changes occur.
27
38
  *
28
39
  * **By default, there will be no reactive state subscriptions** and you must opt-in to state
29
- * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
30
- * full control over when your component updates. Only when you provide a selector will the
31
- * component re-render when the selected state values change.
40
+ * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
41
+ * re-renders and gives you full control over when your component updates.
32
42
  *
33
43
  * Available state properties:
34
44
  * - `canLeadingExecute`: Whether the debouncer can execute on the leading edge
@@ -45,7 +55,14 @@ let __tanstack_pacer_debouncer = require("@tanstack/pacer/debouncer");
45
55
  * { wait: 500 }
46
56
  * );
47
57
  *
48
- * // Opt-in to re-render when isPending changes (optimized for loading states)
58
+ * // Subscribe to state changes deep in component tree using Subscribe HOC
59
+ * <searchDebouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>
60
+ * {({ isPending }) => (
61
+ * <div>{isPending ? 'Searching...' : 'Ready'}</div>
62
+ * )}
63
+ * </searchDebouncer.Subscribe>
64
+ *
65
+ * // Opt-in to re-render when isPending changes at hook level (optimized for loading states)
49
66
  * const searchDebouncer = useDebouncer(
50
67
  * (query: string) => fetchSearchResults(query),
51
68
  * { wait: 500 },
@@ -84,7 +101,14 @@ function useDebouncer(fn, options, selector = () => ({})) {
84
101
  ...require_PacerProvider.useDefaultPacerOptions().debouncer,
85
102
  ...options
86
103
  };
87
- const [debouncer] = (0, preact_hooks.useState)(() => new __tanstack_pacer_debouncer.Debouncer(fn, mergedOptions));
104
+ const [debouncer] = (0, preact_hooks.useState)(() => {
105
+ const debouncerInstance = new _tanstack_pacer_debouncer.Debouncer(fn, mergedOptions);
106
+ debouncerInstance.Subscribe = function Subscribe(props) {
107
+ const selected = (0, _tanstack_preact_store.useStore)(debouncerInstance.store, props.selector);
108
+ return typeof props.children === "function" ? props.children(selected) : props.children;
109
+ };
110
+ return debouncerInstance;
111
+ });
88
112
  debouncer.fn = fn;
89
113
  debouncer.setOptions(mergedOptions);
90
114
  (0, preact_hooks.useEffect)(() => {
@@ -92,7 +116,7 @@ function useDebouncer(fn, options, selector = () => ({})) {
92
116
  debouncer.cancel();
93
117
  };
94
118
  }, [debouncer]);
95
- const state = (0, __tanstack_preact_store.useStore)(debouncer.store, selector);
119
+ const state = (0, _tanstack_preact_store.useStore)(debouncer.store, selector);
96
120
  return (0, preact_hooks.useMemo)(() => ({
97
121
  ...debouncer,
98
122
  state
@@ -1 +1 @@
1
- {"version":3,"file":"useDebouncer.cjs","names":["useDefaultPacerOptions","Debouncer"],"sources":["../../src/debouncer/useDebouncer.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'preact/hooks'\nimport { Debouncer } from '@tanstack/pacer/debouncer'\nimport { useStore } from '@tanstack/preact-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/preact-store'\nimport type {\n DebouncerOptions,\n DebouncerState,\n} from '@tanstack/pacer/debouncer'\nimport type { AnyFunction } from '@tanstack/pacer/types'\n\nexport interface PreactDebouncer<\n TFn extends AnyFunction,\n TSelected = {},\n> extends Omit<Debouncer<TFn>, 'store'> {\n /**\n * Reactive state that will be updated and re-rendered when the debouncer state changes\n *\n * Use this instead of `debouncer.store.state`\n */\n readonly state: Readonly<TSelected>\n /**\n * @deprecated Use `debouncer.state` instead of `debouncer.store.state` if you want to read reactive state.\n * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.\n * Although, you can make the state reactive by using the `useStore` in your own usage.\n */\n readonly store: Store<Readonly<DebouncerState<TFn>>>\n}\n\n/**\n * A Preact hook that creates and manages a Debouncer instance.\n *\n * This is a lower-level hook that provides direct access to the Debouncer's functionality without\n * any built-in state management. This allows you to integrate it with any state management solution\n * you prefer (useState, Redux, Zustand, etc.).\n *\n * This hook provides debouncing functionality to limit how often a function can be called,\n * waiting for a specified delay before executing the latest call. This is useful for handling\n * frequent events like window resizing, scroll events, or real-time search inputs.\n *\n * The debouncer will only execute the function after the specified wait time has elapsed\n * since the last call. If the function is called again before the wait time expires, the\n * timer resets and starts waiting again.\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you\n * to specify which state changes will trigger a re-render, optimizing performance by preventing\n * unnecessary re-renders when irrelevant state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function. This prevents unnecessary re-renders and gives you\n * full control over when your component updates. Only when you provide a selector will the\n * component re-render when the selected state values change.\n *\n * Available state properties:\n * - `canLeadingExecute`: Whether the debouncer can execute on the leading edge\n * - `executionCount`: Number of function executions that have been completed\n * - `isPending`: Whether the debouncer is waiting for the timeout to trigger execution\n * - `lastArgs`: The arguments from the most recent call to maybeExecute\n * - `status`: Current execution status ('disabled' | 'idle' | 'pending')\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 }\n * );\n *\n * // Opt-in to re-render when isPending changes (optimized for loading states)\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 },\n * (state) => ({ isPending: state.isPending })\n * );\n *\n * // Opt-in to re-render when executionCount changes (optimized for tracking execution)\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 },\n * (state) => ({ executionCount: state.executionCount })\n * );\n *\n * // Multiple state properties - re-render when any of these change\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 },\n * (state) => ({\n * isPending: state.isPending,\n * executionCount: state.executionCount,\n * status: state.status\n * })\n * );\n *\n * // In an event handler\n * const handleChange = (e) => {\n * searchDebouncer.maybeExecute(e.target.value);\n * };\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { isPending } = searchDebouncer.state;\n * ```\n */\nexport function useDebouncer<TFn extends AnyFunction, TSelected = {}>(\n fn: TFn,\n options: DebouncerOptions<TFn>,\n selector: (state: DebouncerState<TFn>) => TSelected = () => ({}) as TSelected,\n): PreactDebouncer<TFn, TSelected> {\n const mergedOptions = {\n ...useDefaultPacerOptions().debouncer,\n ...options,\n } as DebouncerOptions<TFn>\n\n const [debouncer] = useState(() => new Debouncer(fn, mergedOptions))\n\n debouncer.fn = fn\n debouncer.setOptions(mergedOptions)\n\n useEffect(() => {\n return () => {\n debouncer.cancel()\n }\n }, [debouncer])\n\n const state = useStore(debouncer.store, selector)\n\n return useMemo(\n () =>\n ({\n ...debouncer,\n state,\n }) as PreactDebouncer<TFn, TSelected>, // omit `store` in favor of `state`\n [debouncer, state],\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwGA,SAAgB,aACd,IACA,SACA,kBAA6D,EAAE,GAC9B;CACjC,MAAM,gBAAgB;EACpB,GAAGA,8CAAwB,CAAC;EAC5B,GAAG;EACJ;CAED,MAAM,CAAC,8CAA4B,IAAIC,qCAAU,IAAI,cAAc,CAAC;AAEpE,WAAU,KAAK;AACf,WAAU,WAAW,cAAc;AAEnC,mCAAgB;AACd,eAAa;AACX,aAAU,QAAQ;;IAEnB,CAAC,UAAU,CAAC;CAEf,MAAM,8CAAiB,UAAU,OAAO,SAAS;AAEjD,yCAEK;EACC,GAAG;EACH;EACD,GACH,CAAC,WAAW,MAAM,CACnB"}
1
+ {"version":3,"file":"useDebouncer.cjs","names":["useDefaultPacerOptions","Debouncer"],"sources":["../../src/debouncer/useDebouncer.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'preact/hooks'\nimport { Debouncer } from '@tanstack/pacer/debouncer'\nimport { useStore } from '@tanstack/preact-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/preact-store'\nimport type {\n DebouncerOptions,\n DebouncerState,\n} from '@tanstack/pacer/debouncer'\nimport type { AnyFunction } from '@tanstack/pacer/types'\nimport type { ComponentChildren } from 'preact'\n\nexport interface PreactDebouncer<\n TFn extends AnyFunction,\n TSelected = {},\n> extends Omit<Debouncer<TFn>, 'store'> {\n /**\n * A Preact HOC (Higher Order Component) that allows you to subscribe to the debouncer state.\n *\n * This is useful for opting into state re-renders for specific parts of the debouncer state\n * deep in your component tree without needing to pass a selector to the hook.\n *\n * @example\n * <debouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>\n * {({ isPending }) => (\n * <div>{isPending ? 'Loading...' : 'Ready'}</div>\n * )}\n * </debouncer.Subscribe>\n */\n Subscribe: <TSelected>(props: {\n selector: (state: DebouncerState<TFn>) => TSelected\n children: ((state: TSelected) => ComponentChildren) | ComponentChildren\n }) => ComponentChildren\n /**\n * Reactive state that will be updated and re-rendered when the debouncer state changes\n *\n * Use this instead of `debouncer.store.state`\n */\n readonly state: Readonly<TSelected>\n /**\n * @deprecated Use `debouncer.state` instead of `debouncer.store.state` if you want to read reactive state.\n * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.\n * Although, you can make the state reactive by using the `useStore` in your own usage.\n */\n readonly store: Store<Readonly<DebouncerState<TFn>>>\n}\n\n/**\n * A Preact hook that creates and manages a Debouncer instance.\n *\n * This is a lower-level hook that provides direct access to the Debouncer's functionality without\n * any built-in state management. This allows you to integrate it with any state management solution\n * you prefer (useState, Redux, Zustand, etc.).\n *\n * This hook provides debouncing functionality to limit how often a function can be called,\n * waiting for a specified delay before executing the latest call. This is useful for handling\n * frequent events like window resizing, scroll events, or real-time search inputs.\n *\n * The debouncer will only execute the function after the specified wait time has elapsed\n * since the last call. If the function is called again before the wait time expires, the\n * timer resets and starts waiting again.\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. You can subscribe to state changes\n * in two ways:\n *\n * **1. Using `debouncer.Subscribe` HOC (Recommended for component tree subscriptions)**\n *\n * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without\n * needing to pass a selector to the hook. This is ideal when you want to subscribe to state\n * in child components.\n *\n * **2. Using the `selector` parameter (For hook-level subscriptions)**\n *\n * The `selector` parameter allows you to specify which state changes will trigger a re-render\n * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant\n * state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary\n * re-renders and gives you full control over when your component updates.\n *\n * Available state properties:\n * - `canLeadingExecute`: Whether the debouncer can execute on the leading edge\n * - `executionCount`: Number of function executions that have been completed\n * - `isPending`: Whether the debouncer is waiting for the timeout to trigger execution\n * - `lastArgs`: The arguments from the most recent call to maybeExecute\n * - `status`: Current execution status ('disabled' | 'idle' | 'pending')\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 }\n * );\n *\n * // Subscribe to state changes deep in component tree using Subscribe HOC\n * <searchDebouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>\n * {({ isPending }) => (\n * <div>{isPending ? 'Searching...' : 'Ready'}</div>\n * )}\n * </searchDebouncer.Subscribe>\n *\n * // Opt-in to re-render when isPending changes at hook level (optimized for loading states)\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 },\n * (state) => ({ isPending: state.isPending })\n * );\n *\n * // Opt-in to re-render when executionCount changes (optimized for tracking execution)\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 },\n * (state) => ({ executionCount: state.executionCount })\n * );\n *\n * // Multiple state properties - re-render when any of these change\n * const searchDebouncer = useDebouncer(\n * (query: string) => fetchSearchResults(query),\n * { wait: 500 },\n * (state) => ({\n * isPending: state.isPending,\n * executionCount: state.executionCount,\n * status: state.status\n * })\n * );\n *\n * // In an event handler\n * const handleChange = (e) => {\n * searchDebouncer.maybeExecute(e.target.value);\n * };\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { isPending } = searchDebouncer.state;\n * ```\n */\nexport function useDebouncer<TFn extends AnyFunction, TSelected = {}>(\n fn: TFn,\n options: DebouncerOptions<TFn>,\n selector: (state: DebouncerState<TFn>) => TSelected = () => ({}) as TSelected,\n): PreactDebouncer<TFn, TSelected> {\n const mergedOptions = {\n ...useDefaultPacerOptions().debouncer,\n ...options,\n } as DebouncerOptions<TFn>\n\n const [debouncer] = useState(() => {\n const debouncerInstance = new Debouncer(\n fn,\n mergedOptions,\n ) as unknown as PreactDebouncer<TFn, TSelected>\n\n debouncerInstance.Subscribe = function Subscribe<TSelected>(props: {\n selector: (state: DebouncerState<TFn>) => TSelected\n children: ((state: TSelected) => ComponentChildren) | ComponentChildren\n }) {\n const selected = useStore(debouncerInstance.store, props.selector)\n\n return typeof props.children === 'function'\n ? props.children(selected)\n : props.children\n }\n\n return debouncerInstance\n })\n\n debouncer.fn = fn\n debouncer.setOptions(mergedOptions)\n\n useEffect(() => {\n return () => {\n debouncer.cancel()\n }\n }, [debouncer])\n\n const state = useStore(debouncer.store, selector)\n\n return useMemo(\n () =>\n ({\n ...debouncer,\n state,\n }) as PreactDebouncer<TFn, TSelected>, // omit `store` in favor of `state`\n [debouncer, state],\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2IA,SAAgB,aACd,IACA,SACA,kBAA6D,EAAE,GAC9B;CACjC,MAAM,gBAAgB;EACpB,GAAGA,8CAAwB,CAAC;EAC5B,GAAG;EACJ;CAED,MAAM,CAAC,8CAA4B;EACjC,MAAM,oBAAoB,IAAIC,oCAC5B,IACA,cACD;AAED,oBAAkB,YAAY,SAAS,UAAqB,OAGzD;GACD,MAAM,gDAAoB,kBAAkB,OAAO,MAAM,SAAS;AAElE,UAAO,OAAO,MAAM,aAAa,aAC7B,MAAM,SAAS,SAAS,GACxB,MAAM;;AAGZ,SAAO;GACP;AAEF,WAAU,KAAK;AACf,WAAU,WAAW,cAAc;AAEnC,mCAAgB;AACd,eAAa;AACX,aAAU,QAAQ;;IAEnB,CAAC,UAAU,CAAC;CAEf,MAAM,6CAAiB,UAAU,OAAO,SAAS;AAEjD,yCAEK;EACC,GAAG;EACH;EACD,GACH,CAAC,WAAW,MAAM,CACnB"}
@@ -1,9 +1,27 @@
1
1
  import { Store } from "@tanstack/preact-store";
2
+ import { ComponentChildren } from "preact";
2
3
  import { AnyFunction } from "@tanstack/pacer/types";
3
4
  import { Debouncer, DebouncerOptions, DebouncerState } from "@tanstack/pacer/debouncer";
4
5
 
5
6
  //#region src/debouncer/useDebouncer.d.ts
6
7
  interface PreactDebouncer<TFn extends AnyFunction, TSelected = {}> extends Omit<Debouncer<TFn>, 'store'> {
8
+ /**
9
+ * A Preact HOC (Higher Order Component) that allows you to subscribe to the debouncer state.
10
+ *
11
+ * This is useful for opting into state re-renders for specific parts of the debouncer state
12
+ * deep in your component tree without needing to pass a selector to the hook.
13
+ *
14
+ * @example
15
+ * <debouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>
16
+ * {({ isPending }) => (
17
+ * <div>{isPending ? 'Loading...' : 'Ready'}</div>
18
+ * )}
19
+ * </debouncer.Subscribe>
20
+ */
21
+ Subscribe: <TSelected>(props: {
22
+ selector: (state: DebouncerState<TFn>) => TSelected;
23
+ children: ((state: TSelected) => ComponentChildren) | ComponentChildren;
24
+ }) => ComponentChildren;
7
25
  /**
8
26
  * Reactive state that will be updated and re-rendered when the debouncer state changes
9
27
  *
@@ -34,14 +52,24 @@ interface PreactDebouncer<TFn extends AnyFunction, TSelected = {}> extends Omit<
34
52
  *
35
53
  * ## State Management and Selector
36
54
  *
37
- * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
38
- * to specify which state changes will trigger a re-render, optimizing performance by preventing
39
- * unnecessary re-renders when irrelevant state changes occur.
55
+ * The hook uses TanStack Store for reactive state management. You can subscribe to state changes
56
+ * in two ways:
57
+ *
58
+ * **1. Using `debouncer.Subscribe` HOC (Recommended for component tree subscriptions)**
59
+ *
60
+ * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
61
+ * needing to pass a selector to the hook. This is ideal when you want to subscribe to state
62
+ * in child components.
63
+ *
64
+ * **2. Using the `selector` parameter (For hook-level subscriptions)**
65
+ *
66
+ * The `selector` parameter allows you to specify which state changes will trigger a re-render
67
+ * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
68
+ * state changes occur.
40
69
  *
41
70
  * **By default, there will be no reactive state subscriptions** and you must opt-in to state
42
- * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
43
- * full control over when your component updates. Only when you provide a selector will the
44
- * component re-render when the selected state values change.
71
+ * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
72
+ * re-renders and gives you full control over when your component updates.
45
73
  *
46
74
  * Available state properties:
47
75
  * - `canLeadingExecute`: Whether the debouncer can execute on the leading edge
@@ -58,7 +86,14 @@ interface PreactDebouncer<TFn extends AnyFunction, TSelected = {}> extends Omit<
58
86
  * { wait: 500 }
59
87
  * );
60
88
  *
61
- * // Opt-in to re-render when isPending changes (optimized for loading states)
89
+ * // Subscribe to state changes deep in component tree using Subscribe HOC
90
+ * <searchDebouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>
91
+ * {({ isPending }) => (
92
+ * <div>{isPending ? 'Searching...' : 'Ready'}</div>
93
+ * )}
94
+ * </searchDebouncer.Subscribe>
95
+ *
96
+ * // Opt-in to re-render when isPending changes at hook level (optimized for loading states)
62
97
  * const searchDebouncer = useDebouncer(
63
98
  * (query: string) => fetchSearchResults(query),
64
99
  * { wait: 500 },