Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/spicy-clowns-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@tanstack/preact-pacer': minor
'@tanstack/react-pacer': minor
'@tanstack/solid-pacer': minor
---

add Subscribe API to all util hooks
74 changes: 74 additions & 0 deletions docs/framework/preact/adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,80 @@ import { PacerProvider } from '@tanstack/preact-pacer'

All hooks within the provider will automatically use these default options, which can be overridden on a per-hook basis.

## Subscribing to State

The Preact Adapter supports subscribing to state changes in two ways:

### Using the Subscribe Component

Use the `Subscribe` component to subscribe to state changes deep in your component tree without needing to pass a selector to the hook. This is ideal when you want to subscribe to state in child components.

```tsx
import { useRateLimiter } from '@tanstack/preact-pacer'

function ApiComponent() {
const rateLimiter = useRateLimiter(
(data: string) => {
return fetch('/api/endpoint', {
method: 'POST',
body: JSON.stringify({ data }),
})
},
{ limit: 5, window: 60000 }
)

return (
<div>
<button onClick={() => rateLimiter.maybeExecute('some data')}>
Submit
</button>

<rateLimiter.Subscribe selector={(state) => ({ rejectionCount: state.rejectionCount })}>
{({ rejectionCount }) => (
<div>Rejections: {rejectionCount}</div>
)}
</rateLimiter.Subscribe>
</div>
)
}
```

### Using the Selector Parameter

The `selector` parameter allows you to specify which state changes will trigger reactive updates at the hook level, optimizing performance by preventing unnecessary updates when irrelevant state changes occur.

**By default, `hook.state` is empty (`{}`) as the selector is empty by default.** You must opt-in to state tracking by providing a selector function.

```tsx
import { useDebouncer } from '@tanstack/preact-pacer'

function SearchComponent() {
// Default behavior - no reactive state subscriptions
const debouncer = useDebouncer(
(query: string) => fetchSearchResults(query),
{ wait: 500 }
)
console.log(debouncer.state) // {}

// Opt-in to track isPending changes
const debouncer = useDebouncer(
(query: string) => fetchSearchResults(query),
{ wait: 500 },
(state) => ({ isPending: state.isPending })
)
console.log(debouncer.state.isPending) // Reactive value

return (
<input
onChange={(e) => debouncer.maybeExecute(e.target.value)}
placeholder="Search..."
/>
)
}
```

For more details on state management and available state properties, see the individual guide pages for each utility (e.g., [Rate Limiting Guide](../../guides/rate-limiting.md), [Debouncing Guide](../../guides/debouncing.md)).

## Examples

### Debouncer Example
Expand Down
37 changes: 27 additions & 10 deletions docs/framework/preact/reference/functions/useAsyncBatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ title: useAsyncBatcher
function useAsyncBatcher<TValue, TSelected>(
fn,
options,
selector): ReactAsyncBatcher<TValue, TSelected>;
selector): PreactAsyncBatcher<TValue, TSelected>;
```

Defined in: [preact-pacer/src/async-batcher/useAsyncBatcher.ts:170](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-batcher/useAsyncBatcher.ts#L170)
Defined in: [preact-pacer/src/async-batcher/useAsyncBatcher.ts:205](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-batcher/useAsyncBatcher.ts#L205)

A Preact hook that creates an `AsyncBatcher` instance for managing asynchronous batches of items.

Expand Down Expand Up @@ -44,14 +44,24 @@ Error Handling:

## State Management and Selector

The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
to specify which state changes will trigger a re-render, optimizing performance by preventing
unnecessary re-renders when irrelevant state changes occur.
The hook uses TanStack Store for reactive state management. You can subscribe to state changes
in two ways:

**1. Using `batcher.Subscribe` HOC (Recommended for component tree subscriptions)**

Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
needing to pass a selector to the hook. This is ideal when you want to subscribe to state
in child components.

**2. Using the `selector` parameter (For hook-level subscriptions)**

The `selector` parameter allows you to specify which state changes will trigger a re-render
at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
state changes occur.

**By default, there will be no reactive state subscriptions** and you must opt-in to state
tracking by providing a selector function. This prevents unnecessary re-renders and gives you
full control over when your component updates. Only when you provide a selector will the
component re-render when the selected state values change.
tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
re-renders and gives you full control over when your component updates.

Available state properties:
- `errorCount`: Number of batch executions that have resulted in errors
Expand Down Expand Up @@ -96,7 +106,7 @@ Available state properties:

## Returns

[`ReactAsyncBatcher`](../interfaces/ReactAsyncBatcher.md)\<`TValue`, `TSelected`\>
[`PreactAsyncBatcher`](../interfaces/PreactAsyncBatcher.md)\<`TValue`, `TSelected`\>

## Example

Expand All @@ -110,7 +120,14 @@ const asyncBatcher = useAsyncBatcher(
{ maxSize: 10, wait: 2000 }
);

// Opt-in to re-render when execution state changes (optimized for loading indicators)
// Subscribe to state changes deep in component tree using Subscribe HOC
<asyncBatcher.Subscribe selector={(state) => ({ size: state.size })}>
{({ size }) => (
<div>Batch Size: {size}</div>
)}
</asyncBatcher.Subscribe>

// Opt-in to re-render when execution state changes at hook level (optimized for loading indicators)
const asyncBatcher = useAsyncBatcher(
async (items) => {
const results = await Promise.all(items.map(item => processItem(item)));
Expand Down
37 changes: 27 additions & 10 deletions docs/framework/preact/reference/functions/useAsyncDebouncer.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ title: useAsyncDebouncer
function useAsyncDebouncer<TFn, TSelected>(
fn,
options,
selector): ReactAsyncDebouncer<TFn, TSelected>;
selector): PreactAsyncDebouncer<TFn, TSelected>;
```

Defined in: [preact-pacer/src/async-debouncer/useAsyncDebouncer.ts:150](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-debouncer/useAsyncDebouncer.ts#L150)
Defined in: [preact-pacer/src/async-debouncer/useAsyncDebouncer.ts:185](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-debouncer/useAsyncDebouncer.ts#L185)

A low-level Preact hook that creates an `AsyncDebouncer` instance to delay execution of an async function.

Expand All @@ -39,14 +39,24 @@ Error Handling:

## State Management and Selector

The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
to specify which state changes will trigger a re-render, optimizing performance by preventing
unnecessary re-renders when irrelevant state changes occur.
The hook uses TanStack Store for reactive state management. You can subscribe to state changes
in two ways:

**1. Using `debouncer.Subscribe` HOC (Recommended for component tree subscriptions)**

Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
needing to pass a selector to the hook. This is ideal when you want to subscribe to state
in child components.

**2. Using the `selector` parameter (For hook-level subscriptions)**

The `selector` parameter allows you to specify which state changes will trigger a re-render
at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
state changes occur.

**By default, there will be no reactive state subscriptions** and you must opt-in to state
tracking by providing a selector function. This prevents unnecessary re-renders and gives you
full control over when your component updates. Only when you provide a selector will the
component re-render when the selected state values change.
tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
re-renders and gives you full control over when your component updates.

Available state properties:
- `canLeadingExecute`: Whether the debouncer can execute on the leading edge
Expand Down Expand Up @@ -86,7 +96,7 @@ Available state properties:

## Returns

[`ReactAsyncDebouncer`](../interfaces/ReactAsyncDebouncer.md)\<`TFn`, `TSelected`\>
[`PreactAsyncDebouncer`](../interfaces/PreactAsyncDebouncer.md)\<`TFn`, `TSelected`\>

## Example

Expand All @@ -100,7 +110,14 @@ const searchDebouncer = useAsyncDebouncer(
{ wait: 500 }
);

// Opt-in to re-render when execution state changes (optimized for loading indicators)
// Subscribe to state changes deep in component tree using Subscribe HOC
<searchDebouncer.Subscribe selector={(state) => ({ isPending: state.isPending })}>
{({ isPending }) => (
<div>{isPending ? 'Searching...' : 'Ready'}</div>
)}
</searchDebouncer.Subscribe>

// Opt-in to re-render when execution state changes at hook level (optimized for loading indicators)
const searchDebouncer = useAsyncDebouncer(
async (query: string) => {
const results = await api.search(query);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ title: useAsyncQueuedState
function useAsyncQueuedState<TValue, TSelected>(
fn,
options,
selector?): [TValue[], ReactAsyncQueuer<TValue, TSelected>];
selector?): [TValue[], PreactAsyncQueuer<TValue, TSelected>];
```

Defined in: [preact-pacer/src/async-queuer/useAsyncQueuedState.ts:151](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-queuer/useAsyncQueuedState.ts#L151)
Expand Down Expand Up @@ -88,7 +88,7 @@ Available async queuer state properties:

## Returns

\[`TValue`[], [`ReactAsyncQueuer`](../interfaces/ReactAsyncQueuer.md)\<`TValue`, `TSelected`\>\]
\[`TValue`[], [`PreactAsyncQueuer`](../interfaces/PreactAsyncQueuer.md)\<`TValue`, `TSelected`\>\]

## Example

Expand Down
37 changes: 27 additions & 10 deletions docs/framework/preact/reference/functions/useAsyncQueuer.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ title: useAsyncQueuer
function useAsyncQueuer<TValue, TSelected>(
fn,
options,
selector): ReactAsyncQueuer<TValue, TSelected>;
selector): PreactAsyncQueuer<TValue, TSelected>;
```

Defined in: [preact-pacer/src/async-queuer/useAsyncQueuer.ts:170](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-queuer/useAsyncQueuer.ts#L170)
Defined in: [preact-pacer/src/async-queuer/useAsyncQueuer.ts:205](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-queuer/useAsyncQueuer.ts#L205)

A lower-level Preact hook that creates an `AsyncQueuer` instance for managing an async queue of items.

Expand All @@ -37,14 +37,24 @@ Error Handling:

## State Management and Selector

The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
to specify which state changes will trigger a re-render, optimizing performance by preventing
unnecessary re-renders when irrelevant state changes occur.
The hook uses TanStack Store for reactive state management. You can subscribe to state changes
in two ways:

**1. Using `queuer.Subscribe` HOC (Recommended for component tree subscriptions)**

Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
needing to pass a selector to the hook. This is ideal when you want to subscribe to state
in child components.

**2. Using the `selector` parameter (For hook-level subscriptions)**

The `selector` parameter allows you to specify which state changes will trigger a re-render
at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
state changes occur.

**By default, there will be no reactive state subscriptions** and you must opt-in to state
tracking by providing a selector function. This prevents unnecessary re-renders and gives you
full control over when your component updates. Only when you provide a selector will the
component re-render when the selected state values change.
tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
re-renders and gives you full control over when your component updates.

Available state properties:
- `activeItems`: Items currently being processed by the queuer
Expand Down Expand Up @@ -91,7 +101,7 @@ Available state properties:

## Returns

[`ReactAsyncQueuer`](../interfaces/ReactAsyncQueuer.md)\<`TValue`, `TSelected`\>
[`PreactAsyncQueuer`](../interfaces/PreactAsyncQueuer.md)\<`TValue`, `TSelected`\>

## Example

Expand All @@ -105,7 +115,14 @@ const asyncQueuer = useAsyncQueuer(
{ concurrency: 2, maxSize: 100, started: false }
);

// Opt-in to re-render when queue size changes (optimized for displaying queue length)
// Subscribe to state changes deep in component tree using Subscribe HOC
<asyncQueuer.Subscribe selector={(state) => ({ size: state.size })}>
{({ size }) => (
<div>Queue Size: {size}</div>
)}
</asyncQueuer.Subscribe>

// Opt-in to re-render when queue size changes at hook level (optimized for displaying queue length)
const asyncQueuer = useAsyncQueuer(
async (item) => {
const result = await processItem(item);
Expand Down
37 changes: 27 additions & 10 deletions docs/framework/preact/reference/functions/useAsyncRateLimiter.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ title: useAsyncRateLimiter
function useAsyncRateLimiter<TFn, TSelected>(
fn,
options,
selector): ReactAsyncRateLimiter<TFn, TSelected>;
selector): PreactAsyncRateLimiter<TFn, TSelected>;
```

Defined in: [preact-pacer/src/async-rate-limiter/useAsyncRateLimiter.ts:179](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-rate-limiter/useAsyncRateLimiter.ts#L179)
Defined in: [preact-pacer/src/async-rate-limiter/useAsyncRateLimiter.ts:214](https://github.com/TanStack/pacer/blob/main/packages/preact-pacer/src/async-rate-limiter/useAsyncRateLimiter.ts#L214)

A low-level Preact hook that creates an `AsyncRateLimiter` instance to limit how many times an async function can execute within a time window.

Expand Down Expand Up @@ -43,14 +43,24 @@ Error Handling:

## State Management and Selector

The hook uses TanStack Store for reactive state management. The `selector` parameter allows you
to specify which state changes will trigger a re-render, optimizing performance by preventing
unnecessary re-renders when irrelevant state changes occur.
The hook uses TanStack Store for reactive state management. You can subscribe to state changes
in two ways:

**1. Using `rateLimiter.Subscribe` HOC (Recommended for component tree subscriptions)**

Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without
needing to pass a selector to the hook. This is ideal when you want to subscribe to state
in child components.

**2. Using the `selector` parameter (For hook-level subscriptions)**

The `selector` parameter allows you to specify which state changes will trigger a re-render
at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant
state changes occur.

**By default, there will be no reactive state subscriptions** and you must opt-in to state
tracking by providing a selector function. This prevents unnecessary re-renders and gives you
full control over when your component updates. Only when you provide a selector will the
component re-render when the selected state values change.
tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary
re-renders and gives you full control over when your component updates.

Available state properties:
- `errorCount`: Number of function executions that have resulted in errors
Expand Down Expand Up @@ -88,7 +98,7 @@ Available state properties:

## Returns

[`ReactAsyncRateLimiter`](../interfaces/ReactAsyncRateLimiter.md)\<`TFn`, `TSelected`\>
[`PreactAsyncRateLimiter`](../interfaces/PreactAsyncRateLimiter.md)\<`TFn`, `TSelected`\>

## Example

Expand All @@ -102,7 +112,14 @@ const asyncRateLimiter = useAsyncRateLimiter(
{ limit: 5, window: 1000 } // 5 calls per second
);

// Opt-in to re-render when execution state changes (optimized for loading indicators)
// Subscribe to state changes deep in component tree using Subscribe HOC
<asyncRateLimiter.Subscribe selector={(state) => ({ rejectionCount: state.rejectionCount })}>
{({ rejectionCount }) => (
<div>Rejections: {rejectionCount}</div>
)}
</asyncRateLimiter.Subscribe>

// Opt-in to re-render when execution state changes at hook level (optimized for loading indicators)
const asyncRateLimiter = useAsyncRateLimiter(
async (id: string) => {
const data = await api.fetchData(id);
Expand Down
Loading
Loading