@tanstack/solid-pacer 0.17.0 → 0.18.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/createAsyncBatcher.cjs +26 -12
- package/dist/async-batcher/createAsyncBatcher.cjs.map +1 -1
- package/dist/async-batcher/createAsyncBatcher.d.cts +36 -9
- package/dist/async-batcher/createAsyncBatcher.d.ts +36 -9
- package/dist/async-batcher/createAsyncBatcher.js +22 -8
- package/dist/async-batcher/createAsyncBatcher.js.map +1 -1
- package/dist/async-batcher/index.cjs +3 -3
- package/dist/async-debouncer/createAsyncDebouncer.cjs +26 -12
- package/dist/async-debouncer/createAsyncDebouncer.cjs.map +1 -1
- package/dist/async-debouncer/createAsyncDebouncer.d.cts +36 -9
- package/dist/async-debouncer/createAsyncDebouncer.d.ts +36 -9
- package/dist/async-debouncer/createAsyncDebouncer.js +22 -8
- package/dist/async-debouncer/createAsyncDebouncer.js.map +1 -1
- package/dist/async-debouncer/index.cjs +3 -3
- package/dist/async-queuer/createAsyncQueuer.cjs +26 -12
- package/dist/async-queuer/createAsyncQueuer.cjs.map +1 -1
- package/dist/async-queuer/createAsyncQueuer.d.cts +36 -9
- package/dist/async-queuer/createAsyncQueuer.d.ts +36 -9
- package/dist/async-queuer/createAsyncQueuer.js +22 -8
- package/dist/async-queuer/createAsyncQueuer.js.map +1 -1
- package/dist/async-queuer/index.cjs +3 -3
- package/dist/async-rate-limiter/createAsyncRateLimiter.cjs +89 -30
- package/dist/async-rate-limiter/createAsyncRateLimiter.cjs.map +1 -1
- package/dist/async-rate-limiter/createAsyncRateLimiter.d.cts +99 -27
- package/dist/async-rate-limiter/createAsyncRateLimiter.d.ts +99 -27
- package/dist/async-rate-limiter/createAsyncRateLimiter.js +85 -26
- package/dist/async-rate-limiter/createAsyncRateLimiter.js.map +1 -1
- package/dist/async-rate-limiter/index.cjs +3 -3
- package/dist/async-throttler/createAsyncThrottler.cjs +26 -12
- package/dist/async-throttler/createAsyncThrottler.cjs.map +1 -1
- package/dist/async-throttler/createAsyncThrottler.d.cts +36 -9
- package/dist/async-throttler/createAsyncThrottler.d.ts +36 -9
- package/dist/async-throttler/createAsyncThrottler.js +22 -8
- package/dist/async-throttler/createAsyncThrottler.js.map +1 -1
- package/dist/async-throttler/index.cjs +3 -3
- package/dist/batcher/createBatcher.cjs +26 -12
- package/dist/batcher/createBatcher.cjs.map +1 -1
- package/dist/batcher/createBatcher.d.cts +36 -9
- package/dist/batcher/createBatcher.d.ts +36 -9
- package/dist/batcher/createBatcher.js +22 -8
- package/dist/batcher/createBatcher.js.map +1 -1
- package/dist/batcher/index.cjs +3 -3
- package/dist/debouncer/createDebouncer.cjs +27 -13
- package/dist/debouncer/createDebouncer.cjs.map +1 -1
- package/dist/debouncer/createDebouncer.d.cts +37 -10
- package/dist/debouncer/createDebouncer.d.ts +37 -10
- package/dist/debouncer/createDebouncer.js +23 -9
- package/dist/debouncer/createDebouncer.js.map +1 -1
- package/dist/debouncer/index.cjs +3 -3
- package/dist/index.cjs +3 -3
- package/dist/queuer/createQueuedSignal.cjs +8 -8
- package/dist/queuer/createQueuedSignal.cjs.map +1 -1
- package/dist/queuer/createQueuedSignal.d.cts +8 -8
- package/dist/queuer/createQueuedSignal.d.ts +8 -8
- package/dist/queuer/createQueuedSignal.js +8 -8
- package/dist/queuer/createQueuedSignal.js.map +1 -1
- package/dist/queuer/createQueuer.cjs +26 -12
- package/dist/queuer/createQueuer.cjs.map +1 -1
- package/dist/queuer/createQueuer.d.cts +36 -9
- package/dist/queuer/createQueuer.d.ts +36 -9
- package/dist/queuer/createQueuer.js +22 -8
- package/dist/queuer/createQueuer.js.map +1 -1
- package/dist/queuer/index.cjs +3 -3
- package/dist/rate-limiter/createRateLimiter.cjs +78 -23
- package/dist/rate-limiter/createRateLimiter.cjs.map +1 -1
- package/dist/rate-limiter/createRateLimiter.d.cts +88 -20
- package/dist/rate-limiter/createRateLimiter.d.ts +88 -20
- package/dist/rate-limiter/createRateLimiter.js +74 -19
- package/dist/rate-limiter/createRateLimiter.js.map +1 -1
- package/dist/rate-limiter/index.cjs +3 -3
- package/dist/throttler/createThrottledSignal.cjs +1 -1
- package/dist/throttler/createThrottledSignal.cjs.map +1 -1
- package/dist/throttler/createThrottledSignal.d.cts +1 -1
- package/dist/throttler/createThrottledSignal.d.ts +1 -1
- package/dist/throttler/createThrottledSignal.js +1 -1
- package/dist/throttler/createThrottledSignal.js.map +1 -1
- package/dist/throttler/createThrottledValue.cjs +1 -1
- package/dist/throttler/createThrottledValue.cjs.map +1 -1
- package/dist/throttler/createThrottledValue.d.cts +1 -1
- package/dist/throttler/createThrottledValue.d.ts +1 -1
- package/dist/throttler/createThrottledValue.js +1 -1
- package/dist/throttler/createThrottledValue.js.map +1 -1
- package/dist/throttler/createThrottler.cjs +34 -13
- package/dist/throttler/createThrottler.cjs.map +1 -1
- package/dist/throttler/createThrottler.d.cts +44 -10
- package/dist/throttler/createThrottler.d.ts +44 -10
- package/dist/throttler/createThrottler.js +30 -9
- package/dist/throttler/createThrottler.js.map +1 -1
- package/dist/throttler/index.cjs +3 -3
- package/dist/types/index.cjs +3 -3
- package/dist/utils/index.cjs +3 -3
- package/package.json +1 -1
- package/src/async-batcher/createAsyncBatcher.ts +51 -10
- package/src/async-debouncer/createAsyncDebouncer.ts +51 -10
- package/src/async-queuer/createAsyncQueuer.ts +51 -10
- package/src/async-rate-limiter/createAsyncRateLimiter.ts +114 -28
- package/src/async-throttler/createAsyncThrottler.ts +51 -10
- package/src/batcher/createBatcher.ts +51 -10
- package/src/debouncer/createDebouncer.ts +52 -11
- package/src/queuer/createQueuedSignal.ts +8 -8
- package/src/queuer/createQueuer.ts +51 -10
- package/src/rate-limiter/createRateLimiter.ts +103 -21
- package/src/throttler/createThrottledSignal.ts +1 -1
- package/src/throttler/createThrottledValue.ts +1 -1
- package/src/throttler/createThrottler.ts +59 -11
|
@@ -2,7 +2,7 @@ import { AsyncQueuer } from '@tanstack/pacer/async-queuer'
|
|
|
2
2
|
import { useStore } from '@tanstack/solid-store'
|
|
3
3
|
import { useDefaultPacerOptions } from '../provider/PacerProvider'
|
|
4
4
|
import type { Store } from '@tanstack/solid-store'
|
|
5
|
-
import type { Accessor } from 'solid-js'
|
|
5
|
+
import type { Accessor, JSX } from 'solid-js'
|
|
6
6
|
import type {
|
|
7
7
|
AsyncQueuerOptions,
|
|
8
8
|
AsyncQueuerState,
|
|
@@ -12,6 +12,23 @@ export interface SolidAsyncQueuer<TValue, TSelected = {}> extends Omit<
|
|
|
12
12
|
AsyncQueuer<TValue>,
|
|
13
13
|
'store'
|
|
14
14
|
> {
|
|
15
|
+
/**
|
|
16
|
+
* A Solid component that allows you to subscribe to the queuer state.
|
|
17
|
+
*
|
|
18
|
+
* This is useful for tracking specific parts of the queuer state
|
|
19
|
+
* deep in your component tree without needing to pass a selector to the hook.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <queuer.Subscribe selector={(state) => ({ pendingItems: state.pendingItems, activeItems: state.activeItems })}>
|
|
23
|
+
* {(state) => (
|
|
24
|
+
* <div>Pending: {state().pendingItems.length}, Active: {state().activeItems.length}</div>
|
|
25
|
+
* )}
|
|
26
|
+
* </queuer.Subscribe>
|
|
27
|
+
*/
|
|
28
|
+
Subscribe: <TSelected>(props: {
|
|
29
|
+
selector: (state: AsyncQueuerState<TValue>) => TSelected
|
|
30
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
31
|
+
}) => JSX.Element
|
|
15
32
|
/**
|
|
16
33
|
* Reactive state that will be updated when the queuer state changes
|
|
17
34
|
*
|
|
@@ -51,14 +68,24 @@ export interface SolidAsyncQueuer<TValue, TSelected = {}> extends Omit<
|
|
|
51
68
|
*
|
|
52
69
|
* ## State Management and Selector
|
|
53
70
|
*
|
|
54
|
-
* The hook uses TanStack Store for reactive state management.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
71
|
+
* The hook uses TanStack Store for reactive state management. You can subscribe to state changes
|
|
72
|
+
* in two ways:
|
|
73
|
+
*
|
|
74
|
+
* **1. Using `queuer.Subscribe` component (Recommended for component tree subscriptions)**
|
|
75
|
+
*
|
|
76
|
+
* Use the `Subscribe` component to subscribe to state changes deep in your component tree without
|
|
77
|
+
* needing to pass a selector to the hook. This is ideal when you want to subscribe to state
|
|
78
|
+
* in child components.
|
|
79
|
+
*
|
|
80
|
+
* **2. Using the `selector` parameter (For hook-level subscriptions)**
|
|
81
|
+
*
|
|
82
|
+
* The `selector` parameter allows you to specify which state changes will trigger reactive updates
|
|
83
|
+
* at the hook level, optimizing performance by preventing unnecessary updates when irrelevant
|
|
84
|
+
* state changes occur.
|
|
57
85
|
*
|
|
58
86
|
* **By default, there will be no reactive state subscriptions** and you must opt-in to state
|
|
59
|
-
* tracking by providing a selector function. This prevents unnecessary
|
|
60
|
-
* full control over when your component
|
|
61
|
-
* component re-render when the selected state values change.
|
|
87
|
+
* tracking by providing a selector function or using the `Subscribe` component. This prevents unnecessary
|
|
88
|
+
* updates and gives you full control over when your component tracks state changes.
|
|
62
89
|
*
|
|
63
90
|
* Available state properties:
|
|
64
91
|
* - `activeItems`: Array of items currently being processed
|
|
@@ -88,7 +115,7 @@ export interface SolidAsyncQueuer<TValue, TSelected = {}> extends Omit<
|
|
|
88
115
|
* }
|
|
89
116
|
* });
|
|
90
117
|
*
|
|
91
|
-
* // Opt-in to
|
|
118
|
+
* // Opt-in to track queue state changes (optimized for UI updates)
|
|
92
119
|
* const asyncQueuer = createAsyncQueuer(
|
|
93
120
|
* async (item) => await fetchData(item),
|
|
94
121
|
* { concurrency: 2, started: true },
|
|
@@ -99,7 +126,7 @@ export interface SolidAsyncQueuer<TValue, TSelected = {}> extends Omit<
|
|
|
99
126
|
* })
|
|
100
127
|
* );
|
|
101
128
|
*
|
|
102
|
-
* // Opt-in to
|
|
129
|
+
* // Opt-in to track processing metrics changes (optimized for tracking progress)
|
|
103
130
|
* const asyncQueuer = createAsyncQueuer(
|
|
104
131
|
* async (item) => await fetchData(item),
|
|
105
132
|
* { concurrency: 2, started: true },
|
|
@@ -131,7 +158,21 @@ export function createAsyncQueuer<TValue, TSelected = {}>(
|
|
|
131
158
|
...options,
|
|
132
159
|
} as AsyncQueuerOptions<TValue>
|
|
133
160
|
|
|
134
|
-
const asyncQueuer = new AsyncQueuer<TValue>(
|
|
161
|
+
const asyncQueuer = new AsyncQueuer<TValue>(
|
|
162
|
+
fn,
|
|
163
|
+
mergedOptions,
|
|
164
|
+
) as unknown as SolidAsyncQueuer<TValue, TSelected>
|
|
165
|
+
|
|
166
|
+
asyncQueuer.Subscribe = function Subscribe<TSelected>(props: {
|
|
167
|
+
selector: (state: AsyncQueuerState<TValue>) => TSelected
|
|
168
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
169
|
+
}) {
|
|
170
|
+
const selected = useStore(asyncQueuer.store, props.selector)
|
|
171
|
+
|
|
172
|
+
return typeof props.children === 'function'
|
|
173
|
+
? props.children(selected)
|
|
174
|
+
: props.children
|
|
175
|
+
}
|
|
135
176
|
|
|
136
177
|
const state = useStore(asyncQueuer.store, selector)
|
|
137
178
|
|
|
@@ -2,7 +2,7 @@ import { AsyncRateLimiter } from '@tanstack/pacer/async-rate-limiter'
|
|
|
2
2
|
import { useStore } from '@tanstack/solid-store'
|
|
3
3
|
import { useDefaultPacerOptions } from '../provider/PacerProvider'
|
|
4
4
|
import type { Store } from '@tanstack/solid-store'
|
|
5
|
-
import type { Accessor } from 'solid-js'
|
|
5
|
+
import type { Accessor, JSX } from 'solid-js'
|
|
6
6
|
import type { AnyAsyncFunction } from '@tanstack/pacer/types'
|
|
7
7
|
import type {
|
|
8
8
|
AsyncRateLimiterOptions,
|
|
@@ -13,6 +13,23 @@ export interface SolidAsyncRateLimiter<
|
|
|
13
13
|
TFn extends AnyAsyncFunction,
|
|
14
14
|
TSelected = {},
|
|
15
15
|
> extends Omit<AsyncRateLimiter<TFn>, 'store'> {
|
|
16
|
+
/**
|
|
17
|
+
* A Solid component that allows you to subscribe to the rate limiter state.
|
|
18
|
+
*
|
|
19
|
+
* This is useful for tracking specific parts of the rate limiter state
|
|
20
|
+
* deep in your component tree without needing to pass a selector to the hook.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* <rateLimiter.Subscribe selector={(state) => ({ rejectionCount: state.rejectionCount, isExecuting: state.isExecuting })}>
|
|
24
|
+
* {(state) => (
|
|
25
|
+
* <div>Rejected: {state().rejectionCount}, {state().isExecuting ? 'Executing' : 'Idle'}</div>
|
|
26
|
+
* )}
|
|
27
|
+
* </rateLimiter.Subscribe>
|
|
28
|
+
*/
|
|
29
|
+
Subscribe: <TSelected>(props: {
|
|
30
|
+
selector: (state: AsyncRateLimiterState<TFn>) => TSelected
|
|
31
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
32
|
+
}) => JSX.Element
|
|
16
33
|
/**
|
|
17
34
|
* Reactive state that will be updated when the rate limiter state changes
|
|
18
35
|
*
|
|
@@ -64,14 +81,24 @@ export interface SolidAsyncRateLimiter<
|
|
|
64
81
|
*
|
|
65
82
|
* ## State Management and Selector
|
|
66
83
|
*
|
|
67
|
-
* The hook uses TanStack Store for reactive state management.
|
|
68
|
-
*
|
|
69
|
-
*
|
|
84
|
+
* The hook uses TanStack Store for reactive state management. You can subscribe to state changes
|
|
85
|
+
* in two ways:
|
|
86
|
+
*
|
|
87
|
+
* **1. Using `rateLimiter.Subscribe` component (Recommended for component tree subscriptions)**
|
|
88
|
+
*
|
|
89
|
+
* Use the `Subscribe` component to subscribe to state changes deep in your component tree without
|
|
90
|
+
* needing to pass a selector to the hook. This is ideal when you want to subscribe to state
|
|
91
|
+
* in child components.
|
|
92
|
+
*
|
|
93
|
+
* **2. Using the `selector` parameter (For hook-level subscriptions)**
|
|
94
|
+
*
|
|
95
|
+
* The `selector` parameter allows you to specify which state changes will trigger reactive updates
|
|
96
|
+
* at the hook level, optimizing performance by preventing unnecessary updates when irrelevant
|
|
97
|
+
* state changes occur.
|
|
70
98
|
*
|
|
71
99
|
* **By default, there will be no reactive state subscriptions** and you must opt-in to state
|
|
72
|
-
* tracking by providing a selector function. This prevents unnecessary
|
|
73
|
-
* full control over when your component
|
|
74
|
-
* component re-render when the selected state values change.
|
|
100
|
+
* tracking by providing a selector function or using the `Subscribe` component. This prevents unnecessary
|
|
101
|
+
* updates and gives you full control over when your component tracks state changes.
|
|
75
102
|
*
|
|
76
103
|
* Available state properties:
|
|
77
104
|
* - `currentWindowStart`: Timestamp when the current window started
|
|
@@ -87,7 +114,7 @@ export interface SolidAsyncRateLimiter<
|
|
|
87
114
|
* @example
|
|
88
115
|
* ```tsx
|
|
89
116
|
* // Default behavior - no reactive state subscriptions
|
|
90
|
-
* const
|
|
117
|
+
* const asyncRateLimiter = createAsyncRateLimiter(
|
|
91
118
|
* async (id: string) => {
|
|
92
119
|
* const data = await api.fetchData(id);
|
|
93
120
|
* return data; // Return value is preserved
|
|
@@ -95,36 +122,81 @@ export interface SolidAsyncRateLimiter<
|
|
|
95
122
|
* { limit: 5, window: 1000 } // 5 calls per second
|
|
96
123
|
* );
|
|
97
124
|
*
|
|
98
|
-
* //
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
125
|
+
* // Subscribe to state changes deep in component tree using Subscribe component
|
|
126
|
+
* <asyncRateLimiter.Subscribe selector={(state) => ({ rejectionCount: state.rejectionCount, isExecuting: state.isExecuting })}>
|
|
127
|
+
* {(state) => (
|
|
128
|
+
* <div>Rejected: {state().rejectionCount}, {state().isExecuting ? 'Executing' : 'Idle'}</div>
|
|
129
|
+
* )}
|
|
130
|
+
* </asyncRateLimiter.Subscribe>
|
|
131
|
+
*
|
|
132
|
+
* // Opt-in to track execution state changes at hook level (optimized for loading indicators)
|
|
133
|
+
* const asyncRateLimiter = createAsyncRateLimiter(
|
|
134
|
+
* async (id: string) => {
|
|
135
|
+
* const data = await api.fetchData(id);
|
|
136
|
+
* return data;
|
|
103
137
|
* },
|
|
104
|
-
* { limit:
|
|
138
|
+
* { limit: 5, window: 1000 },
|
|
139
|
+
* (state) => ({ isExecuting: state.isExecuting })
|
|
140
|
+
* );
|
|
141
|
+
*
|
|
142
|
+
* // Opt-in to track results when available (optimized for data display)
|
|
143
|
+
* const asyncRateLimiter = createAsyncRateLimiter(
|
|
144
|
+
* async (id: string) => {
|
|
145
|
+
* const data = await api.fetchData(id);
|
|
146
|
+
* return data;
|
|
147
|
+
* },
|
|
148
|
+
* { limit: 5, window: 1000 },
|
|
105
149
|
* (state) => ({
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* rejectionCount: state.rejectionCount
|
|
150
|
+
* lastResult: state.lastResult,
|
|
151
|
+
* successCount: state.successCount
|
|
109
152
|
* })
|
|
110
153
|
* );
|
|
111
154
|
*
|
|
112
|
-
* // Opt-in to
|
|
113
|
-
* const
|
|
114
|
-
* async (
|
|
115
|
-
* const
|
|
116
|
-
* return
|
|
155
|
+
* // Opt-in to track error/rejection state changes (optimized for error handling)
|
|
156
|
+
* const asyncRateLimiter = createAsyncRateLimiter(
|
|
157
|
+
* async (id: string) => {
|
|
158
|
+
* const data = await api.fetchData(id);
|
|
159
|
+
* return data;
|
|
117
160
|
* },
|
|
118
161
|
* {
|
|
119
|
-
* limit:
|
|
120
|
-
* window:
|
|
121
|
-
*
|
|
162
|
+
* limit: 5,
|
|
163
|
+
* window: 1000,
|
|
164
|
+
* onError: (error) => console.error('API call failed:', error),
|
|
165
|
+
* onReject: (rateLimiter) => console.log('Rate limit exceeded')
|
|
122
166
|
* },
|
|
123
|
-
* (state) => ({
|
|
167
|
+
* (state) => ({
|
|
168
|
+
* errorCount: state.errorCount,
|
|
169
|
+
* rejectionCount: state.rejectionCount
|
|
170
|
+
* })
|
|
171
|
+
* );
|
|
172
|
+
*
|
|
173
|
+
* // Opt-in to track execution metrics changes (optimized for stats display)
|
|
174
|
+
* const asyncRateLimiter = createAsyncRateLimiter(
|
|
175
|
+
* async (id: string) => {
|
|
176
|
+
* const data = await api.fetchData(id);
|
|
177
|
+
* return data;
|
|
178
|
+
* },
|
|
179
|
+
* { limit: 5, window: 1000 },
|
|
180
|
+
* (state) => ({
|
|
181
|
+
* successCount: state.successCount,
|
|
182
|
+
* errorCount: state.errorCount,
|
|
183
|
+
* settleCount: state.settleCount,
|
|
184
|
+
* rejectionCount: state.rejectionCount
|
|
185
|
+
* })
|
|
186
|
+
* );
|
|
187
|
+
*
|
|
188
|
+
* // Opt-in to track execution times changes (optimized for window calculations)
|
|
189
|
+
* const asyncRateLimiter = createAsyncRateLimiter(
|
|
190
|
+
* async (id: string) => {
|
|
191
|
+
* const data = await api.fetchData(id);
|
|
192
|
+
* return data;
|
|
193
|
+
* },
|
|
194
|
+
* { limit: 5, window: 1000 },
|
|
195
|
+
* (state) => ({ executionTimes: state.executionTimes })
|
|
124
196
|
* );
|
|
125
197
|
*
|
|
126
198
|
* // Access the selected state (will be empty object {} unless selector provided)
|
|
127
|
-
* const {
|
|
199
|
+
* const { isExecuting, lastResult, rejectionCount } = asyncRateLimiter.state();
|
|
128
200
|
* ```
|
|
129
201
|
*/
|
|
130
202
|
export function createAsyncRateLimiter<
|
|
@@ -141,7 +213,21 @@ export function createAsyncRateLimiter<
|
|
|
141
213
|
...options,
|
|
142
214
|
} as AsyncRateLimiterOptions<TFn>
|
|
143
215
|
|
|
144
|
-
const asyncRateLimiter = new AsyncRateLimiter<TFn>(
|
|
216
|
+
const asyncRateLimiter = new AsyncRateLimiter<TFn>(
|
|
217
|
+
fn,
|
|
218
|
+
mergedOptions,
|
|
219
|
+
) as unknown as SolidAsyncRateLimiter<TFn, TSelected>
|
|
220
|
+
|
|
221
|
+
asyncRateLimiter.Subscribe = function Subscribe<TSelected>(props: {
|
|
222
|
+
selector: (state: AsyncRateLimiterState<TFn>) => TSelected
|
|
223
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
224
|
+
}) {
|
|
225
|
+
const selected = useStore(asyncRateLimiter.store, props.selector)
|
|
226
|
+
|
|
227
|
+
return typeof props.children === 'function'
|
|
228
|
+
? props.children(selected)
|
|
229
|
+
: props.children
|
|
230
|
+
}
|
|
145
231
|
|
|
146
232
|
const state = useStore(asyncRateLimiter.store, selector)
|
|
147
233
|
|
|
@@ -2,7 +2,7 @@ import { AsyncThrottler } from '@tanstack/pacer/async-throttler'
|
|
|
2
2
|
import { useStore } from '@tanstack/solid-store'
|
|
3
3
|
import { useDefaultPacerOptions } from '../provider/PacerProvider'
|
|
4
4
|
import type { Store } from '@tanstack/solid-store'
|
|
5
|
-
import type { Accessor } from 'solid-js'
|
|
5
|
+
import type { Accessor, JSX } from 'solid-js'
|
|
6
6
|
import type { AnyAsyncFunction } from '@tanstack/pacer/types'
|
|
7
7
|
import type {
|
|
8
8
|
AsyncThrottlerOptions,
|
|
@@ -13,6 +13,23 @@ export interface SolidAsyncThrottler<
|
|
|
13
13
|
TFn extends AnyAsyncFunction,
|
|
14
14
|
TSelected = {},
|
|
15
15
|
> extends Omit<AsyncThrottler<TFn>, 'store'> {
|
|
16
|
+
/**
|
|
17
|
+
* A Solid component that allows you to subscribe to the throttler state.
|
|
18
|
+
*
|
|
19
|
+
* This is useful for tracking specific parts of the throttler state
|
|
20
|
+
* deep in your component tree without needing to pass a selector to the hook.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* <throttler.Subscribe selector={(state) => ({ isPending: state.isPending, isExecuting: state.isExecuting })}>
|
|
24
|
+
* {(state) => (
|
|
25
|
+
* <div>{state().isPending ? 'Pending...' : state().isExecuting ? 'Executing...' : 'Ready'}</div>
|
|
26
|
+
* )}
|
|
27
|
+
* </throttler.Subscribe>
|
|
28
|
+
*/
|
|
29
|
+
Subscribe: <TSelected>(props: {
|
|
30
|
+
selector: (state: AsyncThrottlerState<TFn>) => TSelected
|
|
31
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
32
|
+
}) => JSX.Element
|
|
16
33
|
/**
|
|
17
34
|
* Reactive state that will be updated when the throttler state changes
|
|
18
35
|
*
|
|
@@ -50,14 +67,24 @@ export interface SolidAsyncThrottler<
|
|
|
50
67
|
*
|
|
51
68
|
* ## State Management and Selector
|
|
52
69
|
*
|
|
53
|
-
* The hook uses TanStack Store for reactive state management.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
70
|
+
* The hook uses TanStack Store for reactive state management. You can subscribe to state changes
|
|
71
|
+
* in two ways:
|
|
72
|
+
*
|
|
73
|
+
* **1. Using `throttler.Subscribe` component (Recommended for component tree subscriptions)**
|
|
74
|
+
*
|
|
75
|
+
* Use the `Subscribe` component to subscribe to state changes deep in your component tree without
|
|
76
|
+
* needing to pass a selector to the hook. This is ideal when you want to subscribe to state
|
|
77
|
+
* in child components.
|
|
78
|
+
*
|
|
79
|
+
* **2. Using the `selector` parameter (For hook-level subscriptions)**
|
|
80
|
+
*
|
|
81
|
+
* The `selector` parameter allows you to specify which state changes will trigger reactive updates
|
|
82
|
+
* at the hook level, optimizing performance by preventing unnecessary updates when irrelevant
|
|
83
|
+
* state changes occur.
|
|
56
84
|
*
|
|
57
85
|
* **By default, there will be no reactive state subscriptions** and you must opt-in to state
|
|
58
|
-
* tracking by providing a selector function. This prevents unnecessary
|
|
59
|
-
* full control over when your component
|
|
60
|
-
* component re-render when the selected state values change.
|
|
86
|
+
* tracking by providing a selector function or using the `Subscribe` component. This prevents unnecessary
|
|
87
|
+
* updates and gives you full control over when your component tracks state changes.
|
|
61
88
|
*
|
|
62
89
|
* Available state properties:
|
|
63
90
|
* - `canLeadingExecute`: Whether the throttler can execute on the leading edge
|
|
@@ -84,7 +111,7 @@ export interface SolidAsyncThrottler<
|
|
|
84
111
|
* { wait: 1000 }
|
|
85
112
|
* );
|
|
86
113
|
*
|
|
87
|
-
* // Opt-in to
|
|
114
|
+
* // Opt-in to track isPending or isExecuting changes (optimized for loading states)
|
|
88
115
|
* const throttler = createAsyncThrottler(
|
|
89
116
|
* async (query) => {
|
|
90
117
|
* const result = await searchAPI(query);
|
|
@@ -94,7 +121,7 @@ export interface SolidAsyncThrottler<
|
|
|
94
121
|
* (state) => ({ isPending: state.isPending, isExecuting: state.isExecuting })
|
|
95
122
|
* );
|
|
96
123
|
*
|
|
97
|
-
* // Opt-in to
|
|
124
|
+
* // Opt-in to track error state changes (optimized for error handling)
|
|
98
125
|
* const throttler = createAsyncThrottler(
|
|
99
126
|
* async (query) => {
|
|
100
127
|
* const result = await searchAPI(query);
|
|
@@ -129,7 +156,21 @@ export function createAsyncThrottler<
|
|
|
129
156
|
...options,
|
|
130
157
|
} as AsyncThrottlerOptions<TFn>
|
|
131
158
|
|
|
132
|
-
const asyncThrottler = new AsyncThrottler(
|
|
159
|
+
const asyncThrottler = new AsyncThrottler(
|
|
160
|
+
fn,
|
|
161
|
+
mergedOptions,
|
|
162
|
+
) as unknown as SolidAsyncThrottler<TFn, TSelected>
|
|
163
|
+
|
|
164
|
+
asyncThrottler.Subscribe = function Subscribe<TSelected>(props: {
|
|
165
|
+
selector: (state: AsyncThrottlerState<TFn>) => TSelected
|
|
166
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
167
|
+
}) {
|
|
168
|
+
const selected = useStore(asyncThrottler.store, props.selector)
|
|
169
|
+
|
|
170
|
+
return typeof props.children === 'function'
|
|
171
|
+
? props.children(selected)
|
|
172
|
+
: props.children
|
|
173
|
+
}
|
|
133
174
|
|
|
134
175
|
const state = useStore(asyncThrottler.store, selector)
|
|
135
176
|
|
|
@@ -2,13 +2,30 @@ import { Batcher } from '@tanstack/pacer/batcher'
|
|
|
2
2
|
import { useStore } from '@tanstack/solid-store'
|
|
3
3
|
import { useDefaultPacerOptions } from '../provider/PacerProvider'
|
|
4
4
|
import type { Store } from '@tanstack/solid-store'
|
|
5
|
-
import type { Accessor } from 'solid-js'
|
|
5
|
+
import type { Accessor, JSX } from 'solid-js'
|
|
6
6
|
import type { BatcherOptions, BatcherState } from '@tanstack/pacer/batcher'
|
|
7
7
|
|
|
8
8
|
export interface SolidBatcher<TValue, TSelected = {}> extends Omit<
|
|
9
9
|
Batcher<TValue>,
|
|
10
10
|
'store'
|
|
11
11
|
> {
|
|
12
|
+
/**
|
|
13
|
+
* A Solid component that allows you to subscribe to the batcher state.
|
|
14
|
+
*
|
|
15
|
+
* This is useful for tracking specific parts of the batcher state
|
|
16
|
+
* deep in your component tree without needing to pass a selector to the hook.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <batcher.Subscribe selector={(state) => ({ size: state.size, isRunning: state.isRunning })}>
|
|
20
|
+
* {(state) => (
|
|
21
|
+
* <div>Batch: {state().size} items, {state().isRunning ? 'Processing' : 'Idle'}</div>
|
|
22
|
+
* )}
|
|
23
|
+
* </batcher.Subscribe>
|
|
24
|
+
*/
|
|
25
|
+
Subscribe: <TSelected>(props: {
|
|
26
|
+
selector: (state: BatcherState<TValue>) => TSelected
|
|
27
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
28
|
+
}) => JSX.Element
|
|
12
29
|
/**
|
|
13
30
|
* Reactive state that will be updated when the batcher state changes
|
|
14
31
|
*
|
|
@@ -40,14 +57,24 @@ export interface SolidBatcher<TValue, TSelected = {}> extends Omit<
|
|
|
40
57
|
*
|
|
41
58
|
* ## State Management and Selector
|
|
42
59
|
*
|
|
43
|
-
* The hook uses TanStack Store for reactive state management.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
60
|
+
* The hook uses TanStack Store for reactive state management. You can subscribe to state changes
|
|
61
|
+
* in two ways:
|
|
62
|
+
*
|
|
63
|
+
* **1. Using `batcher.Subscribe` component (Recommended for component tree subscriptions)**
|
|
64
|
+
*
|
|
65
|
+
* Use the `Subscribe` component to subscribe to state changes deep in your component tree without
|
|
66
|
+
* needing to pass a selector to the hook. This is ideal when you want to subscribe to state
|
|
67
|
+
* in child components.
|
|
68
|
+
*
|
|
69
|
+
* **2. Using the `selector` parameter (For hook-level subscriptions)**
|
|
70
|
+
*
|
|
71
|
+
* The `selector` parameter allows you to specify which state changes will trigger reactive updates
|
|
72
|
+
* at the hook level, optimizing performance by preventing unnecessary updates when irrelevant
|
|
73
|
+
* state changes occur.
|
|
46
74
|
*
|
|
47
75
|
* **By default, there will be no reactive state subscriptions** and you must opt-in to state
|
|
48
|
-
* tracking by providing a selector function. This prevents unnecessary
|
|
49
|
-
* full control over when your component
|
|
50
|
-
* component re-render when the selected state values change.
|
|
76
|
+
* tracking by providing a selector function or using the `Subscribe` component. This prevents unnecessary
|
|
77
|
+
* updates and gives you full control over when your component tracks state changes.
|
|
51
78
|
*
|
|
52
79
|
* Available state properties:
|
|
53
80
|
* - `executionCount`: Number of batch executions that have been completed
|
|
@@ -71,14 +98,14 @@ export interface SolidBatcher<TValue, TSelected = {}> extends Omit<
|
|
|
71
98
|
* }
|
|
72
99
|
* );
|
|
73
100
|
*
|
|
74
|
-
* // Opt-in to
|
|
101
|
+
* // Opt-in to track items or isRunning changes (optimized for UI updates)
|
|
75
102
|
* const batcher = createBatcher(
|
|
76
103
|
* (items) => console.log('Processing batch:', items),
|
|
77
104
|
* { maxSize: 5, wait: 2000 },
|
|
78
105
|
* (state) => ({ items: state.items, isRunning: state.isRunning })
|
|
79
106
|
* );
|
|
80
107
|
*
|
|
81
|
-
* // Opt-in to
|
|
108
|
+
* // Opt-in to track execution metrics changes (optimized for tracking progress)
|
|
82
109
|
* const batcher = createBatcher(
|
|
83
110
|
* (items) => console.log('Processing batch:', items),
|
|
84
111
|
* { maxSize: 5, wait: 2000 },
|
|
@@ -111,7 +138,21 @@ export function createBatcher<TValue, TSelected = {}>(
|
|
|
111
138
|
...options,
|
|
112
139
|
} as BatcherOptions<TValue>
|
|
113
140
|
|
|
114
|
-
const batcher = new Batcher(fn, mergedOptions)
|
|
141
|
+
const batcher = new Batcher(fn, mergedOptions) as unknown as SolidBatcher<
|
|
142
|
+
TValue,
|
|
143
|
+
TSelected
|
|
144
|
+
>
|
|
145
|
+
|
|
146
|
+
batcher.Subscribe = function Subscribe<TSelected>(props: {
|
|
147
|
+
selector: (state: BatcherState<TValue>) => TSelected
|
|
148
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
149
|
+
}) {
|
|
150
|
+
const selected = useStore(batcher.store, props.selector)
|
|
151
|
+
|
|
152
|
+
return typeof props.children === 'function'
|
|
153
|
+
? props.children(selected)
|
|
154
|
+
: props.children
|
|
155
|
+
}
|
|
115
156
|
|
|
116
157
|
const state = useStore(batcher.store, selector)
|
|
117
158
|
return {
|
|
@@ -3,7 +3,7 @@ import { createEffect, onCleanup } from 'solid-js'
|
|
|
3
3
|
import { useStore } from '@tanstack/solid-store'
|
|
4
4
|
import { useDefaultPacerOptions } from '../provider/PacerProvider'
|
|
5
5
|
import type { Store } from '@tanstack/solid-store'
|
|
6
|
-
import type { Accessor } from 'solid-js'
|
|
6
|
+
import type { Accessor, JSX } from 'solid-js'
|
|
7
7
|
import type { AnyFunction } from '@tanstack/pacer/types'
|
|
8
8
|
import type {
|
|
9
9
|
DebouncerOptions,
|
|
@@ -14,6 +14,23 @@ export interface SolidDebouncer<
|
|
|
14
14
|
TFn extends AnyFunction,
|
|
15
15
|
TSelected = {},
|
|
16
16
|
> extends Omit<Debouncer<TFn>, 'store'> {
|
|
17
|
+
/**
|
|
18
|
+
* A Solid component that allows you to subscribe to the debouncer state.
|
|
19
|
+
*
|
|
20
|
+
* This is useful for tracking specific parts of the debouncer state
|
|
21
|
+
* deep in your component tree without needing to pass a selector to the hook.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* <debouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>
|
|
25
|
+
* {(state) => (
|
|
26
|
+
* <div>{state().isPending ? 'Waiting...' : 'Ready'}</div>
|
|
27
|
+
* )}
|
|
28
|
+
* </debouncer.Subscribe>
|
|
29
|
+
*/
|
|
30
|
+
Subscribe: <TSelected>(props: {
|
|
31
|
+
selector: (state: DebouncerState<TFn>) => TSelected
|
|
32
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
33
|
+
}) => JSX.Element
|
|
17
34
|
/**
|
|
18
35
|
* Reactive state that will be updated when the debouncer state changes
|
|
19
36
|
*
|
|
@@ -45,14 +62,24 @@ export interface SolidDebouncer<
|
|
|
45
62
|
*
|
|
46
63
|
* ## State Management and Selector
|
|
47
64
|
*
|
|
48
|
-
* The hook uses TanStack Store for reactive state management.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
65
|
+
* The hook uses TanStack Store for reactive state management. You can subscribe to state changes
|
|
66
|
+
* in two ways:
|
|
67
|
+
*
|
|
68
|
+
* **1. Using `debouncer.Subscribe` component (Recommended for component tree subscriptions)**
|
|
69
|
+
*
|
|
70
|
+
* Use the `Subscribe` component to subscribe to state changes deep in your component tree without
|
|
71
|
+
* needing to pass a selector to the hook. This is ideal when you want to subscribe to state
|
|
72
|
+
* in child components.
|
|
73
|
+
*
|
|
74
|
+
* **2. Using the `selector` parameter (For hook-level subscriptions)**
|
|
75
|
+
*
|
|
76
|
+
* The `selector` parameter allows you to specify which state changes will trigger reactive updates
|
|
77
|
+
* at the hook level, optimizing performance by preventing unnecessary updates when irrelevant
|
|
78
|
+
* state changes occur.
|
|
51
79
|
*
|
|
52
80
|
* **By default, there will be no reactive state subscriptions** and you must opt-in to state
|
|
53
|
-
* tracking by providing a selector function. This prevents unnecessary
|
|
54
|
-
* full control over when your component
|
|
55
|
-
* component re-render when the selected state values change.
|
|
81
|
+
* tracking by providing a selector function or using the `Subscribe` component. This prevents unnecessary
|
|
82
|
+
* updates and gives you full control over when your component tracks state changes.
|
|
56
83
|
*
|
|
57
84
|
* Available state properties:
|
|
58
85
|
* - `canLeadingExecute`: Whether the debouncer can execute on the leading edge
|
|
@@ -69,21 +96,21 @@ export interface SolidDebouncer<
|
|
|
69
96
|
* { wait: 500 }
|
|
70
97
|
* );
|
|
71
98
|
*
|
|
72
|
-
* // Opt-in to
|
|
99
|
+
* // Opt-in to track isPending changes (optimized for loading states)
|
|
73
100
|
* const debouncer = createDebouncer(
|
|
74
101
|
* (query: string) => fetchSearchResults(query),
|
|
75
102
|
* { wait: 500 },
|
|
76
103
|
* (state) => ({ isPending: state.isPending })
|
|
77
104
|
* );
|
|
78
105
|
*
|
|
79
|
-
* // Opt-in to
|
|
106
|
+
* // Opt-in to track executionCount changes (optimized for tracking execution)
|
|
80
107
|
* const debouncer = createDebouncer(
|
|
81
108
|
* (query: string) => fetchSearchResults(query),
|
|
82
109
|
* { wait: 500 },
|
|
83
110
|
* (state) => ({ executionCount: state.executionCount })
|
|
84
111
|
* );
|
|
85
112
|
*
|
|
86
|
-
* // Multiple state properties -
|
|
113
|
+
* // Multiple state properties - track when any of these change
|
|
87
114
|
* const debouncer = createDebouncer(
|
|
88
115
|
* (query: string) => fetchSearchResults(query),
|
|
89
116
|
* { wait: 500 },
|
|
@@ -113,7 +140,21 @@ export function createDebouncer<TFn extends AnyFunction, TSelected = {}>(
|
|
|
113
140
|
...options,
|
|
114
141
|
} as DebouncerOptions<TFn>
|
|
115
142
|
|
|
116
|
-
const asyncDebouncer = new Debouncer<TFn>(
|
|
143
|
+
const asyncDebouncer = new Debouncer<TFn>(
|
|
144
|
+
fn,
|
|
145
|
+
mergedOptions,
|
|
146
|
+
) as unknown as SolidDebouncer<TFn, TSelected>
|
|
147
|
+
|
|
148
|
+
asyncDebouncer.Subscribe = function Subscribe<TSelected>(props: {
|
|
149
|
+
selector: (state: DebouncerState<TFn>) => TSelected
|
|
150
|
+
children: ((state: Accessor<TSelected>) => JSX.Element) | JSX.Element
|
|
151
|
+
}) {
|
|
152
|
+
const selected = useStore(asyncDebouncer.store, props.selector)
|
|
153
|
+
|
|
154
|
+
return typeof props.children === 'function'
|
|
155
|
+
? props.children(selected)
|
|
156
|
+
: props.children
|
|
157
|
+
}
|
|
117
158
|
|
|
118
159
|
const state = useStore(asyncDebouncer.store, selector)
|
|
119
160
|
|
|
@@ -20,13 +20,13 @@ import type { QueuerOptions, QueuerState } from '@tanstack/pacer/queuer'
|
|
|
20
20
|
* ## State Management and Selector
|
|
21
21
|
*
|
|
22
22
|
* The primitive uses Solid's reactive state management via the underlying queuer instance.
|
|
23
|
-
* The `selector` parameter allows you to specify which queuer state changes will trigger
|
|
24
|
-
* optimizing performance by preventing unnecessary
|
|
23
|
+
* The `selector` parameter allows you to specify which queuer state changes will trigger reactive updates,
|
|
24
|
+
* optimizing performance by preventing unnecessary updates when irrelevant state changes occur.
|
|
25
25
|
*
|
|
26
26
|
* **By default, there will be no reactive state subscriptions** and you must opt-in to state
|
|
27
|
-
* tracking by providing a selector function. This prevents unnecessary
|
|
28
|
-
* full control over when your component
|
|
29
|
-
* component
|
|
27
|
+
* tracking by providing a selector function. This prevents unnecessary updates and gives you
|
|
28
|
+
* full control over when your component tracks state changes. Only when you provide a selector will the
|
|
29
|
+
* component track changes to the selected state values.
|
|
30
30
|
*
|
|
31
31
|
* Available queuer state properties:
|
|
32
32
|
* - `executionCount`: Number of items that have been processed by the queuer
|
|
@@ -55,7 +55,7 @@ import type { QueuerOptions, QueuerState } from '@tanstack/pacer/queuer'
|
|
|
55
55
|
* }
|
|
56
56
|
* );
|
|
57
57
|
*
|
|
58
|
-
* // Opt-in to
|
|
58
|
+
* // Opt-in to track queue contents changes (optimized for displaying queue items)
|
|
59
59
|
* const [items, addItem, queue] = createQueuedSignal(
|
|
60
60
|
* (item) => console.log('Processing:', item),
|
|
61
61
|
* { started: true, wait: 1000 },
|
|
@@ -66,7 +66,7 @@ import type { QueuerOptions, QueuerState } from '@tanstack/pacer/queuer'
|
|
|
66
66
|
* })
|
|
67
67
|
* );
|
|
68
68
|
*
|
|
69
|
-
* // Opt-in to
|
|
69
|
+
* // Opt-in to track processing state changes (optimized for loading indicators)
|
|
70
70
|
* const [items, addItem, queue] = createQueuedSignal(
|
|
71
71
|
* (item) => console.log('Processing:', item),
|
|
72
72
|
* { started: true, wait: 1000 },
|
|
@@ -78,7 +78,7 @@ import type { QueuerOptions, QueuerState } from '@tanstack/pacer/queuer'
|
|
|
78
78
|
* })
|
|
79
79
|
* );
|
|
80
80
|
*
|
|
81
|
-
* // Opt-in to
|
|
81
|
+
* // Opt-in to track execution metrics changes (optimized for stats display)
|
|
82
82
|
* const [items, addItem, queue] = createQueuedSignal(
|
|
83
83
|
* (item) => console.log('Processing:', item),
|
|
84
84
|
* { started: true, wait: 1000 },
|