@tanstack/solid-pacer 0.9.1 → 0.11.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 (101) hide show
  1. package/dist/cjs/async-batcher/createAsyncBatcher.cjs +1 -1
  2. package/dist/cjs/async-batcher/createAsyncBatcher.cjs.map +1 -1
  3. package/dist/cjs/async-batcher/createAsyncBatcher.d.cts +54 -6
  4. package/dist/cjs/async-debouncer/createAsyncDebouncer.cjs +1 -1
  5. package/dist/cjs/async-debouncer/createAsyncDebouncer.cjs.map +1 -1
  6. package/dist/cjs/async-debouncer/createAsyncDebouncer.d.cts +50 -8
  7. package/dist/cjs/async-queuer/createAsyncQueuer.cjs +1 -1
  8. package/dist/cjs/async-queuer/createAsyncQueuer.cjs.map +1 -1
  9. package/dist/cjs/async-queuer/createAsyncQueuer.d.cts +55 -6
  10. package/dist/cjs/async-rate-limiter/createAsyncRateLimiter.cjs +1 -1
  11. package/dist/cjs/async-rate-limiter/createAsyncRateLimiter.cjs.map +1 -1
  12. package/dist/cjs/async-rate-limiter/createAsyncRateLimiter.d.cts +59 -9
  13. package/dist/cjs/async-throttler/createAsyncThrottler.cjs +1 -1
  14. package/dist/cjs/async-throttler/createAsyncThrottler.cjs.map +1 -1
  15. package/dist/cjs/async-throttler/createAsyncThrottler.d.cts +54 -9
  16. package/dist/cjs/batcher/createBatcher.cjs +1 -1
  17. package/dist/cjs/batcher/createBatcher.cjs.map +1 -1
  18. package/dist/cjs/batcher/createBatcher.d.cts +46 -9
  19. package/dist/cjs/debouncer/createDebouncedSignal.cjs.map +1 -1
  20. package/dist/cjs/debouncer/createDebouncedSignal.d.cts +36 -4
  21. package/dist/cjs/debouncer/createDebouncedValue.cjs.map +1 -1
  22. package/dist/cjs/debouncer/createDebouncedValue.d.cts +30 -2
  23. package/dist/cjs/debouncer/createDebouncer.cjs +1 -1
  24. package/dist/cjs/debouncer/createDebouncer.cjs.map +1 -1
  25. package/dist/cjs/debouncer/createDebouncer.d.cts +56 -10
  26. package/dist/cjs/queuer/createQueuer.cjs +1 -1
  27. package/dist/cjs/queuer/createQueuer.cjs.map +1 -1
  28. package/dist/cjs/queuer/createQueuer.d.cts +46 -12
  29. package/dist/cjs/rate-limiter/createRateLimitedSignal.cjs.map +1 -1
  30. package/dist/cjs/rate-limiter/createRateLimitedSignal.d.cts +30 -3
  31. package/dist/cjs/rate-limiter/createRateLimitedValue.cjs.map +1 -1
  32. package/dist/cjs/rate-limiter/createRateLimitedValue.d.cts +32 -2
  33. package/dist/cjs/rate-limiter/createRateLimiter.cjs +1 -1
  34. package/dist/cjs/rate-limiter/createRateLimiter.cjs.map +1 -1
  35. package/dist/cjs/rate-limiter/createRateLimiter.d.cts +50 -8
  36. package/dist/cjs/throttler/createThrottledSignal.cjs.map +1 -1
  37. package/dist/cjs/throttler/createThrottledSignal.d.cts +34 -6
  38. package/dist/cjs/throttler/createThrottledValue.cjs.map +1 -1
  39. package/dist/cjs/throttler/createThrottledValue.d.cts +33 -2
  40. package/dist/cjs/throttler/createThrottler.cjs +1 -1
  41. package/dist/cjs/throttler/createThrottler.cjs.map +1 -1
  42. package/dist/cjs/throttler/createThrottler.d.cts +56 -12
  43. package/dist/esm/async-batcher/createAsyncBatcher.d.ts +54 -6
  44. package/dist/esm/async-batcher/createAsyncBatcher.js +1 -1
  45. package/dist/esm/async-batcher/createAsyncBatcher.js.map +1 -1
  46. package/dist/esm/async-debouncer/createAsyncDebouncer.d.ts +50 -8
  47. package/dist/esm/async-debouncer/createAsyncDebouncer.js +1 -1
  48. package/dist/esm/async-debouncer/createAsyncDebouncer.js.map +1 -1
  49. package/dist/esm/async-queuer/createAsyncQueuer.d.ts +55 -6
  50. package/dist/esm/async-queuer/createAsyncQueuer.js +1 -1
  51. package/dist/esm/async-queuer/createAsyncQueuer.js.map +1 -1
  52. package/dist/esm/async-rate-limiter/createAsyncRateLimiter.d.ts +59 -9
  53. package/dist/esm/async-rate-limiter/createAsyncRateLimiter.js +1 -1
  54. package/dist/esm/async-rate-limiter/createAsyncRateLimiter.js.map +1 -1
  55. package/dist/esm/async-throttler/createAsyncThrottler.d.ts +54 -9
  56. package/dist/esm/async-throttler/createAsyncThrottler.js +1 -1
  57. package/dist/esm/async-throttler/createAsyncThrottler.js.map +1 -1
  58. package/dist/esm/batcher/createBatcher.d.ts +46 -9
  59. package/dist/esm/batcher/createBatcher.js +1 -1
  60. package/dist/esm/batcher/createBatcher.js.map +1 -1
  61. package/dist/esm/debouncer/createDebouncedSignal.d.ts +36 -4
  62. package/dist/esm/debouncer/createDebouncedSignal.js.map +1 -1
  63. package/dist/esm/debouncer/createDebouncedValue.d.ts +30 -2
  64. package/dist/esm/debouncer/createDebouncedValue.js.map +1 -1
  65. package/dist/esm/debouncer/createDebouncer.d.ts +56 -10
  66. package/dist/esm/debouncer/createDebouncer.js +1 -1
  67. package/dist/esm/debouncer/createDebouncer.js.map +1 -1
  68. package/dist/esm/queuer/createQueuer.d.ts +46 -12
  69. package/dist/esm/queuer/createQueuer.js +1 -1
  70. package/dist/esm/queuer/createQueuer.js.map +1 -1
  71. package/dist/esm/rate-limiter/createRateLimitedSignal.d.ts +30 -3
  72. package/dist/esm/rate-limiter/createRateLimitedSignal.js.map +1 -1
  73. package/dist/esm/rate-limiter/createRateLimitedValue.d.ts +32 -2
  74. package/dist/esm/rate-limiter/createRateLimitedValue.js.map +1 -1
  75. package/dist/esm/rate-limiter/createRateLimiter.d.ts +50 -8
  76. package/dist/esm/rate-limiter/createRateLimiter.js +1 -1
  77. package/dist/esm/rate-limiter/createRateLimiter.js.map +1 -1
  78. package/dist/esm/throttler/createThrottledSignal.d.ts +34 -6
  79. package/dist/esm/throttler/createThrottledSignal.js.map +1 -1
  80. package/dist/esm/throttler/createThrottledValue.d.ts +33 -2
  81. package/dist/esm/throttler/createThrottledValue.js.map +1 -1
  82. package/dist/esm/throttler/createThrottler.d.ts +56 -12
  83. package/dist/esm/throttler/createThrottler.js +1 -1
  84. package/dist/esm/throttler/createThrottler.js.map +1 -1
  85. package/package.json +2 -2
  86. package/src/async-batcher/createAsyncBatcher.ts +58 -14
  87. package/src/async-debouncer/createAsyncDebouncer.ts +53 -10
  88. package/src/async-queuer/createAsyncQueuer.ts +58 -8
  89. package/src/async-rate-limiter/createAsyncRateLimiter.ts +61 -10
  90. package/src/async-throttler/createAsyncThrottler.ts +56 -10
  91. package/src/batcher/createBatcher.ts +48 -10
  92. package/src/debouncer/createDebouncedSignal.ts +36 -7
  93. package/src/debouncer/createDebouncedValue.ts +30 -5
  94. package/src/debouncer/createDebouncer.ts +58 -17
  95. package/src/queuer/createQueuer.ts +48 -14
  96. package/src/rate-limiter/createRateLimitedSignal.ts +30 -3
  97. package/src/rate-limiter/createRateLimitedValue.ts +32 -2
  98. package/src/rate-limiter/createRateLimiter.ts +53 -16
  99. package/src/throttler/createThrottledSignal.ts +34 -9
  100. package/src/throttler/createThrottledValue.ts +33 -5
  101. package/src/throttler/createThrottler.ts +58 -19
@@ -34,23 +34,53 @@ import type {
34
34
  * For more direct control over rate limiting behavior without Solid state management,
35
35
  * consider using the lower-level createRateLimiter hook instead.
36
36
  *
37
+ * ## State Management and Selector
38
+ *
39
+ * The hook uses TanStack Store for reactive state management via the underlying rate limiter instance.
40
+ * The `selector` parameter allows you to specify which rate limiter state changes will trigger reactive updates,
41
+ * optimizing performance by preventing unnecessary subscriptions when irrelevant state changes occur.
42
+ *
43
+ * **By default, there will be no reactive state subscriptions** and you must opt-in to state
44
+ * tracking by providing a selector function. This prevents unnecessary reactive updates and gives you
45
+ * full control over when your component subscribes to state changes. Only when you provide a selector will
46
+ * the reactive system track the selected state values.
47
+ *
48
+ * Available rate limiter state properties:
49
+ * - `callsInWindow`: Number of calls made in the current window
50
+ * - `remainingInWindow`: Number of calls remaining in the current window
51
+ * - `windowStart`: Unix timestamp when the current window started
52
+ * - `nextWindowStart`: Unix timestamp when the next window will start
53
+ * - `msUntilNextWindow`: Milliseconds until the next window starts
54
+ * - `isAtLimit`: Whether the call limit for the current window has been reached
55
+ * - `status`: Current status ('disabled' | 'idle' | 'at-limit')
56
+ *
37
57
  * @example
38
58
  * ```tsx
39
- * // Basic rate limiting - update at most 5 times per minute with a sliding window
59
+ * // Default behavior - no reactive state subscriptions
40
60
  * const [rateLimitedValue, rateLimiter] = createRateLimitedValue(rawValue, {
41
61
  * limit: 5,
42
62
  * window: 60000,
43
63
  * windowType: 'sliding'
44
64
  * });
45
65
  *
66
+ * // Opt-in to reactive updates when limit state changes (optimized for UI feedback)
67
+ * const [rateLimitedValue, rateLimiter] = createRateLimitedValue(
68
+ * rawValue,
69
+ * { limit: 5, window: 60000 },
70
+ * (state) => ({ isAtLimit: state.isAtLimit, remainingInWindow: state.remainingInWindow })
71
+ * );
72
+ *
46
73
  * // Use the rate-limited value
47
74
  * console.log(rateLimitedValue()); // Access the current rate-limited value
48
75
  *
76
+ * // Access rate limiter state via signals
77
+ * console.log('Is at limit:', rateLimiter.state().isAtLimit);
78
+ *
49
79
  * // Control the rate limiter
50
80
  * rateLimiter.reset(); // Reset the rate limit window
51
81
  * ```
52
82
  */
53
- export function createRateLimitedValue<TValue, TSelected = RateLimiterState>(
83
+ export function createRateLimitedValue<TValue, TSelected = {}>(
54
84
  value: Accessor<TValue>,
55
85
  initialOptions: RateLimiterOptions<Setter<TValue>>,
56
86
  selector?: (state: RateLimiterState) => TSelected,
@@ -1,5 +1,6 @@
1
1
  import { RateLimiter } from '@tanstack/pacer/rate-limiter'
2
2
  import { useStore } from '@tanstack/solid-store'
3
+ import type { Store } from '@tanstack/solid-store'
3
4
  import type { Accessor } from 'solid-js'
4
5
  import type { AnyFunction } from '@tanstack/pacer/types'
5
6
  import type {
@@ -7,16 +8,20 @@ import type {
7
8
  RateLimiterState,
8
9
  } from '@tanstack/pacer/rate-limiter'
9
10
 
10
- export interface SolidRateLimiter<
11
- TFn extends AnyFunction,
12
- TSelected = RateLimiterState,
13
- > extends Omit<RateLimiter<TFn>, 'store'> {
11
+ export interface SolidRateLimiter<TFn extends AnyFunction, TSelected = {}>
12
+ extends Omit<RateLimiter<TFn>, 'store'> {
14
13
  /**
15
14
  * Reactive state that will be updated when the rate limiter state changes
16
15
  *
17
16
  * Use this instead of `rateLimiter.store.state`
18
17
  */
19
18
  readonly state: Accessor<Readonly<TSelected>>
19
+ /**
20
+ * @deprecated Use `rateLimiter.state` instead of `rateLimiter.store.state` if you want to read reactive state.
21
+ * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.
22
+ * Although, you can make the state reactive by using the `useStore` in your own usage.
23
+ */
24
+ readonly store: Store<Readonly<RateLimiterState>>
20
25
  }
21
26
 
22
27
  /**
@@ -40,9 +45,27 @@ export interface SolidRateLimiter<
40
45
  * - Use debouncing when you want to collapse rapid-fire events (e.g. search input)
41
46
  * - Use rate limiting only when you need to enforce hard limits (e.g. API rate limits)
42
47
  *
48
+ * ## State Management and Selector
49
+ *
50
+ * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
51
+ * to specify which state changes will trigger a re-render, optimizing performance by preventing
52
+ * unnecessary re-renders when irrelevant state changes occur.
53
+ *
54
+ * **By default, there will be no reactive state subscriptions** and you must opt-in to state
55
+ * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
56
+ * full control over when your component updates. Only when you provide a selector will the
57
+ * component re-render when the selected state values change.
58
+ *
59
+ * Available state properties:
60
+ * - `executionCount`: Number of function executions that have been completed
61
+ * - `rejectionCount`: Number of function calls that were rejected due to rate limiting
62
+ * - `remainingInWindow`: Number of executions remaining in the current window
63
+ * - `nextWindowTime`: Timestamp when the next window begins
64
+ * - `currentWindowStart`: Timestamp when the current window started
65
+ *
43
66
  * @example
44
67
  * ```tsx
45
- * // Basic rate limiting - max 5 calls per minute with a sliding window
68
+ * // Default behavior - no reactive state subscriptions
46
69
  * const rateLimiter = createRateLimiter(apiCall, {
47
70
  * limit: 5,
48
71
  * window: 60000,
@@ -52,20 +75,34 @@ export interface SolidRateLimiter<
52
75
  * }
53
76
  * });
54
77
  *
55
- * // Access rate limiter state via signals
56
- * console.log('Executions:', rateLimiter.executionCount());
57
- * console.log('Rejections:', rateLimiter.rejectionCount());
58
- * console.log('Remaining:', rateLimiter.remainingInWindow());
59
- * console.log('Next window in:', rateLimiter.msUntilNextWindow());
78
+ * // Opt-in to re-render when rate limit state changes (optimized for UI feedback)
79
+ * const rateLimiter = createRateLimiter(
80
+ * apiCall,
81
+ * { limit: 5, window: 60000 },
82
+ * (state) => ({
83
+ * remainingInWindow: state.remainingInWindow,
84
+ * rejectionCount: state.rejectionCount
85
+ * })
86
+ * );
87
+ *
88
+ * // Opt-in to re-render when execution metrics change (optimized for tracking progress)
89
+ * const rateLimiter = createRateLimiter(
90
+ * apiCall,
91
+ * { limit: 5, window: 60000 },
92
+ * (state) => ({
93
+ * executionCount: state.executionCount,
94
+ * nextWindowTime: state.nextWindowTime
95
+ * })
96
+ * );
97
+ *
98
+ * // Access the selected state (will be empty object {} unless selector provided)
99
+ * const { remainingInWindow, rejectionCount } = rateLimiter.state();
60
100
  * ```
61
101
  */
62
- export function createRateLimiter<
63
- TFn extends AnyFunction,
64
- TSelected = RateLimiterState,
65
- >(
102
+ export function createRateLimiter<TFn extends AnyFunction, TSelected = {}>(
66
103
  fn: TFn,
67
104
  initialOptions: RateLimiterOptions<TFn>,
68
- selector?: (state: RateLimiterState) => TSelected,
105
+ selector: (state: RateLimiterState) => TSelected = () => ({}) as TSelected,
69
106
  ): SolidRateLimiter<TFn, TSelected> {
70
107
  const rateLimiter = new RateLimiter<TFn>(fn, initialOptions)
71
108
 
@@ -74,5 +111,5 @@ export function createRateLimiter<
74
111
  return {
75
112
  ...rateLimiter,
76
113
  state,
77
- } as unknown as SolidRateLimiter<TFn, TSelected> // omit `store` in favor of `state`
114
+ } as SolidRateLimiter<TFn, TSelected> // omit `store` in favor of `state`
78
115
  }
@@ -22,11 +22,39 @@ import type {
22
22
  * For more direct control over throttling without state management,
23
23
  * consider using the lower-level createThrottler hook instead.
24
24
  *
25
+ * ## State Management and Selector
26
+ *
27
+ * The hook uses TanStack Store for reactive state management via the underlying throttler instance.
28
+ * The `selector` parameter allows you to specify which throttler state changes will trigger reactive updates,
29
+ * optimizing performance by preventing unnecessary subscriptions when irrelevant state changes occur.
30
+ *
31
+ * **By default, there will be no reactive state subscriptions** and you must opt-in to state
32
+ * tracking by providing a selector function. This prevents unnecessary reactive updates and gives you
33
+ * full control over when your component subscribes to state changes. Only when you provide a selector will
34
+ * the reactive system track the selected state values.
35
+ *
36
+ * Available throttler state properties:
37
+ * - `canLeadingExecute`: Whether the throttler can execute on the leading edge
38
+ * - `canTrailingExecute`: Whether the throttler can execute on the trailing edge
39
+ * - `executionCount`: Number of function executions that have been completed
40
+ * - `isPending`: Whether the throttler is waiting for the timeout to trigger trailing execution
41
+ * - `lastArgs`: The arguments from the most recent call to maybeExecute
42
+ * - `lastExecutionTime`: Unix timestamp of the last execution
43
+ * - `nextExecutionTime`: Unix timestamp of the next allowed execution
44
+ * - `status`: Current execution status ('disabled' | 'idle' | 'pending')
45
+ *
25
46
  * @example
26
47
  * ```tsx
27
- * // Basic throttling - update state at most once per second
48
+ * // Default behavior - no reactive state subscriptions
28
49
  * const [value, setValue, throttler] = createThrottledSignal(0, { wait: 1000 });
29
50
  *
51
+ * // Opt-in to reactive updates when pending state changes (optimized for loading indicators)
52
+ * const [value, setValue, throttler] = createThrottledSignal(
53
+ * 0,
54
+ * { wait: 1000 },
55
+ * (state) => ({ isPending: state.isPending })
56
+ * );
57
+ *
30
58
  * // With custom leading/trailing behavior
31
59
  * const [value, setValue] = createThrottledSignal(0, {
32
60
  * wait: 1000,
@@ -35,16 +63,13 @@ import type {
35
63
  * });
36
64
  *
37
65
  * // Access throttler state via signals
38
- * console.log('Executions:', throttler.executionCount());
39
- * console.log('Is pending:', throttler.isPending());
40
- * console.log('Last execution:', throttler.lastExecutionTime());
41
- * console.log('Next execution:', throttler.nextExecutionTime());
66
+ * console.log('Executions:', throttler.state().executionCount);
67
+ * console.log('Is pending:', throttler.state().isPending);
68
+ * console.log('Last execution:', throttler.state().lastExecutionTime);
69
+ * console.log('Next execution:', throttler.state().nextExecutionTime);
42
70
  * ```
43
71
  */
44
- export function createThrottledSignal<
45
- TValue,
46
- TSelected = ThrottlerState<Setter<TValue>>,
47
- >(
72
+ export function createThrottledSignal<TValue, TSelected = {}>(
48
73
  value: TValue,
49
74
  initialOptions: ThrottlerOptions<Setter<TValue>>,
50
75
  selector?: (state: ThrottlerState<Setter<TValue>>) => TSelected,
@@ -23,22 +23,50 @@ import type {
23
23
  * For more direct control over throttling behavior without Solid state management,
24
24
  * consider using the lower-level createThrottler hook instead.
25
25
  *
26
+ * ## State Management and Selector
27
+ *
28
+ * The hook uses TanStack Store for reactive state management via the underlying throttler instance.
29
+ * The `selector` parameter allows you to specify which throttler state changes will trigger reactive updates,
30
+ * optimizing performance by preventing unnecessary subscriptions when irrelevant state changes occur.
31
+ *
32
+ * **By default, there will be no reactive state subscriptions** and you must opt-in to state
33
+ * tracking by providing a selector function. This prevents unnecessary reactive updates and gives you
34
+ * full control over when your component subscribes to state changes. Only when you provide a selector will
35
+ * the reactive system track the selected state values.
36
+ *
37
+ * Available throttler state properties:
38
+ * - `canLeadingExecute`: Whether the throttler can execute on the leading edge
39
+ * - `canTrailingExecute`: Whether the throttler can execute on the trailing edge
40
+ * - `executionCount`: Number of function executions that have been completed
41
+ * - `isPending`: Whether the throttler is waiting for the timeout to trigger trailing execution
42
+ * - `lastArgs`: The arguments from the most recent call to maybeExecute
43
+ * - `lastExecutionTime`: Unix timestamp of the last execution
44
+ * - `nextExecutionTime`: Unix timestamp of the next allowed execution
45
+ * - `status`: Current execution status ('disabled' | 'idle' | 'pending')
46
+ *
26
47
  * @example
27
48
  * ```tsx
28
- * // Basic throttling - update at most once per second
49
+ * // Default behavior - no reactive state subscriptions
29
50
  * const [throttledValue, throttler] = createThrottledValue(rawValue, { wait: 1000 });
30
51
  *
52
+ * // Opt-in to reactive updates when pending state changes (optimized for loading indicators)
53
+ * const [throttledValue, throttler] = createThrottledValue(
54
+ * rawValue,
55
+ * { wait: 1000 },
56
+ * (state) => ({ isPending: state.isPending })
57
+ * );
58
+ *
31
59
  * // Use the throttled value
32
60
  * console.log(throttledValue()); // Access the current throttled value
33
61
  *
62
+ * // Access throttler state via signals
63
+ * console.log('Is pending:', throttler.state().isPending);
64
+ *
34
65
  * // Control the throttler
35
66
  * throttler.cancel(); // Cancel any pending updates
36
67
  * ```
37
68
  */
38
- export function createThrottledValue<
39
- TValue,
40
- TSelected = ThrottlerState<Setter<TValue>>,
41
- >(
69
+ export function createThrottledValue<TValue, TSelected = {}>(
42
70
  value: Accessor<TValue>,
43
71
  initialOptions: ThrottlerOptions<Setter<TValue>>,
44
72
  selector?: (state: ThrottlerState<Setter<TValue>>) => TSelected,
@@ -1,6 +1,7 @@
1
1
  import { Throttler } from '@tanstack/pacer/throttler'
2
2
  import { createEffect, onCleanup } from 'solid-js'
3
3
  import { useStore } from '@tanstack/solid-store'
4
+ import type { Store } from '@tanstack/solid-store'
4
5
  import type { Accessor } from 'solid-js'
5
6
  import type { AnyFunction } from '@tanstack/pacer/types'
6
7
  import type {
@@ -8,16 +9,20 @@ import type {
8
9
  ThrottlerState,
9
10
  } from '@tanstack/pacer/throttler'
10
11
 
11
- export interface SolidThrottler<
12
- TFn extends AnyFunction,
13
- TSelected = ThrottlerState<TFn>,
14
- > extends Omit<Throttler<TFn>, 'store'> {
12
+ export interface SolidThrottler<TFn extends AnyFunction, TSelected = {}>
13
+ extends Omit<Throttler<TFn>, 'store'> {
15
14
  /**
16
15
  * Reactive state that will be updated when the throttler state changes
17
16
  *
18
17
  * Use this instead of `throttler.store.state`
19
18
  */
20
19
  readonly state: Accessor<Readonly<TSelected>>
20
+ /**
21
+ * @deprecated Use `throttler.state` instead of `throttler.store.state` if you want to read reactive state.
22
+ * The state on the store object is not reactive, as it has not been wrapped in a `useStore` hook internally.
23
+ * Although, you can make the state reactive by using the `useStore` in your own usage.
24
+ */
25
+ readonly store: Store<Readonly<ThrottlerState<TFn>>>
21
26
  }
22
27
 
23
28
  /**
@@ -31,36 +36,70 @@ export interface SolidThrottler<
31
36
  * regardless of how many times it is called. This is useful for rate-limiting
32
37
  * expensive operations or UI updates.
33
38
  *
39
+ * ## State Management and Selector
40
+ *
41
+ * The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
42
+ * to specify which state changes will trigger a re-render, optimizing performance by preventing
43
+ * unnecessary re-renders when irrelevant state changes occur.
44
+ *
45
+ * **By default, there will be no reactive state subscriptions** and you must opt-in to state
46
+ * tracking by providing a selector function. This prevents unnecessary re-renders and gives you
47
+ * full control over when your component updates. Only when you provide a selector will the
48
+ * component re-render when the selected state values change.
49
+ *
50
+ * Available state properties:
51
+ * - `canLeadingExecute`: Whether the throttler can execute on the leading edge
52
+ * - `canTrailingExecute`: Whether the throttler can execute on the trailing edge
53
+ * - `executionCount`: Number of function executions that have been completed
54
+ * - `isPending`: Whether the throttler is waiting for the timeout to trigger execution
55
+ * - `lastArgs`: The arguments from the most recent call to maybeExecute
56
+ * - `lastExecutionTime`: Timestamp of the last execution
57
+ * - `nextExecutionTime`: Timestamp of the next allowed execution
58
+ * - `status`: Current execution status ('disabled' | 'idle' | 'pending')
59
+ *
34
60
  * @example
35
61
  * ```tsx
36
- * // Basic throttling with custom state
37
- * const [value, setValue] = createSignal(0);
62
+ * // Default behavior - no reactive state subscriptions
38
63
  * const throttler = createThrottler(setValue, { wait: 1000 });
39
64
  *
40
- * // With any state manager
65
+ * // Opt-in to re-render when isPending changes (optimized for loading states)
66
+ * const throttler = createThrottler(
67
+ * setValue,
68
+ * { wait: 1000 },
69
+ * (state) => ({ isPending: state.isPending })
70
+ * );
71
+ *
72
+ * // Opt-in to re-render when executionCount changes (optimized for tracking execution)
73
+ * const throttler = createThrottler(
74
+ * setValue,
75
+ * { wait: 1000 },
76
+ * (state) => ({ executionCount: state.executionCount })
77
+ * );
78
+ *
79
+ * // Multiple state properties - re-render when any of these change
41
80
  * const throttler = createThrottler(
42
- * (value) => stateManager.setState(value),
81
+ * setValue,
43
82
  * {
44
83
  * wait: 2000,
45
84
  * leading: true, // Execute immediately on first call
46
85
  * trailing: false // Skip trailing edge updates
47
- * }
86
+ * },
87
+ * (state) => ({
88
+ * isPending: state.isPending,
89
+ * executionCount: state.executionCount,
90
+ * lastExecutionTime: state.lastExecutionTime,
91
+ * nextExecutionTime: state.nextExecutionTime
92
+ * })
48
93
  * );
49
94
  *
50
- * // Access throttler state via signals
51
- * console.log(throttler.executionCount()); // number of times executed
52
- * console.log(throttler.isPending()); // whether throttled function is pending
53
- * console.log(throttler.lastExecutionTime()); // timestamp of last execution
54
- * console.log(throttler.nextExecutionTime()); // timestamp of next allowed execution
95
+ * // Access the selected state (will be empty object {} unless selector provided)
96
+ * const { isPending, executionCount } = throttler.state();
55
97
  * ```
56
98
  */
57
- export function createThrottler<
58
- TFn extends AnyFunction,
59
- TSelected = ThrottlerState<TFn>,
60
- >(
99
+ export function createThrottler<TFn extends AnyFunction, TSelected = {}>(
61
100
  fn: TFn,
62
101
  initialOptions: ThrottlerOptions<TFn>,
63
- selector?: (state: ThrottlerState<TFn>) => TSelected,
102
+ selector: (state: ThrottlerState<TFn>) => TSelected = () => ({}) as TSelected,
64
103
  ): SolidThrottler<TFn, TSelected> {
65
104
  const asyncThrottler = new Throttler<TFn>(fn, initialOptions)
66
105