@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.
- package/dist/async-batcher/index.cjs +3 -3
- package/dist/async-batcher/index.d.cts +2 -2
- package/dist/async-batcher/index.d.ts +2 -2
- package/dist/async-batcher/useAsyncBatcher.cjs +35 -11
- package/dist/async-batcher/useAsyncBatcher.cjs.map +1 -1
- package/dist/async-batcher/useAsyncBatcher.d.cts +45 -10
- package/dist/async-batcher/useAsyncBatcher.d.ts +45 -10
- package/dist/async-batcher/useAsyncBatcher.js +32 -8
- package/dist/async-batcher/useAsyncBatcher.js.map +1 -1
- package/dist/async-debouncer/index.cjs +3 -3
- package/dist/async-debouncer/index.d.cts +2 -2
- package/dist/async-debouncer/index.d.ts +2 -2
- package/dist/async-debouncer/useAsyncDebouncer.cjs +35 -11
- package/dist/async-debouncer/useAsyncDebouncer.cjs.map +1 -1
- package/dist/async-debouncer/useAsyncDebouncer.d.cts +45 -10
- package/dist/async-debouncer/useAsyncDebouncer.d.ts +45 -10
- package/dist/async-debouncer/useAsyncDebouncer.js +32 -8
- package/dist/async-debouncer/useAsyncDebouncer.js.map +1 -1
- package/dist/async-queuer/index.cjs +3 -3
- package/dist/async-queuer/index.d.cts +2 -2
- package/dist/async-queuer/index.d.ts +2 -2
- package/dist/async-queuer/useAsyncQueuedState.cjs.map +1 -1
- package/dist/async-queuer/useAsyncQueuedState.d.cts +2 -2
- package/dist/async-queuer/useAsyncQueuedState.d.ts +2 -2
- package/dist/async-queuer/useAsyncQueuedState.js.map +1 -1
- package/dist/async-queuer/useAsyncQueuer.cjs +35 -11
- package/dist/async-queuer/useAsyncQueuer.cjs.map +1 -1
- package/dist/async-queuer/useAsyncQueuer.d.cts +45 -10
- package/dist/async-queuer/useAsyncQueuer.d.ts +45 -10
- package/dist/async-queuer/useAsyncQueuer.js +32 -8
- package/dist/async-queuer/useAsyncQueuer.js.map +1 -1
- package/dist/async-rate-limiter/index.cjs +3 -3
- package/dist/async-rate-limiter/index.d.cts +2 -2
- package/dist/async-rate-limiter/index.d.ts +2 -2
- package/dist/async-rate-limiter/useAsyncRateLimiter.cjs +35 -11
- package/dist/async-rate-limiter/useAsyncRateLimiter.cjs.map +1 -1
- package/dist/async-rate-limiter/useAsyncRateLimiter.d.cts +45 -10
- package/dist/async-rate-limiter/useAsyncRateLimiter.d.ts +45 -10
- package/dist/async-rate-limiter/useAsyncRateLimiter.js +32 -8
- package/dist/async-rate-limiter/useAsyncRateLimiter.js.map +1 -1
- package/dist/async-retryer/index.cjs +3 -3
- package/dist/async-throttler/index.cjs +3 -3
- package/dist/async-throttler/index.d.cts +2 -2
- package/dist/async-throttler/index.d.ts +2 -2
- package/dist/async-throttler/useAsyncThrottler.cjs +35 -11
- package/dist/async-throttler/useAsyncThrottler.cjs.map +1 -1
- package/dist/async-throttler/useAsyncThrottler.d.cts +45 -10
- package/dist/async-throttler/useAsyncThrottler.d.ts +45 -10
- package/dist/async-throttler/useAsyncThrottler.js +32 -8
- package/dist/async-throttler/useAsyncThrottler.js.map +1 -1
- package/dist/batcher/index.cjs +3 -3
- package/dist/batcher/useBatcher.cjs +35 -11
- package/dist/batcher/useBatcher.cjs.map +1 -1
- package/dist/batcher/useBatcher.d.cts +42 -7
- package/dist/batcher/useBatcher.d.ts +42 -7
- package/dist/batcher/useBatcher.js +32 -8
- package/dist/batcher/useBatcher.js.map +1 -1
- package/dist/debouncer/index.cjs +3 -3
- package/dist/debouncer/useDebouncer.cjs +35 -11
- package/dist/debouncer/useDebouncer.cjs.map +1 -1
- package/dist/debouncer/useDebouncer.d.cts +42 -7
- package/dist/debouncer/useDebouncer.d.ts +42 -7
- package/dist/debouncer/useDebouncer.js +32 -8
- package/dist/debouncer/useDebouncer.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/provider/PacerProvider.d.cts +1 -1
- package/dist/queuer/index.cjs +3 -3
- package/dist/queuer/useQueuer.cjs +35 -11
- package/dist/queuer/useQueuer.cjs.map +1 -1
- package/dist/queuer/useQueuer.d.cts +42 -7
- package/dist/queuer/useQueuer.d.ts +42 -7
- package/dist/queuer/useQueuer.js +32 -8
- package/dist/queuer/useQueuer.js.map +1 -1
- package/dist/rate-limiter/index.cjs +3 -3
- package/dist/rate-limiter/useRateLimiter.cjs +35 -11
- package/dist/rate-limiter/useRateLimiter.cjs.map +1 -1
- package/dist/rate-limiter/useRateLimiter.d.cts +42 -7
- package/dist/rate-limiter/useRateLimiter.d.ts +42 -7
- package/dist/rate-limiter/useRateLimiter.js +32 -8
- package/dist/rate-limiter/useRateLimiter.js.map +1 -1
- package/dist/throttler/index.cjs +3 -3
- package/dist/throttler/useThrottler.cjs +35 -11
- package/dist/throttler/useThrottler.cjs.map +1 -1
- package/dist/throttler/useThrottler.d.cts +42 -7
- package/dist/throttler/useThrottler.d.ts +42 -7
- package/dist/throttler/useThrottler.js +32 -8
- package/dist/throttler/useThrottler.js.map +1 -1
- package/dist/types/index.cjs +3 -3
- package/dist/utils/index.cjs +3 -3
- package/package.json +1 -1
- package/src/async-batcher/useAsyncBatcher.ts +64 -13
- package/src/async-debouncer/useAsyncDebouncer.ts +64 -13
- package/src/async-queuer/useAsyncQueuedState.ts +2 -2
- package/src/async-queuer/useAsyncQueuer.ts +64 -13
- package/src/async-rate-limiter/useAsyncRateLimiter.ts +64 -13
- package/src/async-throttler/useAsyncThrottler.ts +64 -13
- package/src/batcher/useBatcher.ts +61 -8
- package/src/debouncer/useDebouncer.ts +61 -8
- package/src/queuer/useQueuer.ts +61 -8
- package/src/rate-limiter/useRateLimiter.ts +61 -8
- 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
|
|
4
|
-
let
|
|
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.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
|
27
|
-
* full control over when your component updates.
|
|
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
|
-
* //
|
|
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)(() =>
|
|
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,
|
|
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
|
|
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.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
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
|
|
39
|
-
* full control over when your component updates.
|
|
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
|
-
* //
|
|
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.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
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
|
|
39
|
-
* full control over when your component updates.
|
|
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
|
-
* //
|
|
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.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
|
27
|
-
* full control over when your component updates.
|
|
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
|
-
* //
|
|
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(() =>
|
|
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
|
|
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"}
|
package/dist/debouncer/index.cjs
CHANGED
|
@@ -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
|
|
11
|
-
Object.keys(
|
|
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
|
|
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
|
|
4
|
-
let
|
|
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.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
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
|
|
30
|
-
* full control over when your component updates.
|
|
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
|
-
* //
|
|
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)(() =>
|
|
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,
|
|
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
|
|
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.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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
|
|
43
|
-
* full control over when your component updates.
|
|
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
|
-
* //
|
|
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 },
|