import { LitElement, html, nothing } from 'lit'
import { TanStackDevtoolsCore } from '@tanstack/devtools'
import { pacerDevtoolsPlugin } from '@tanstack/pacer-devtools'
import { createAsyncDebouncer } from '@tanstack/lit-pacer/async-debouncer'
interface SearchResult {
id: number
title: string
}
class Demo extends LitElement {
static properties = { searchTerm: { state: true }, results: { state: true } }
fakeApi = async (term: string): Promise<Array<SearchResult>> => {
await new Promise((resolve) => setTimeout(resolve, 1500))
return [
{ id: 1, title: `${term} result ${Math.floor(Math.random() * 100)}` },
{ id: 2, title: `${term} result ${Math.floor(Math.random() * 100)}` },
{ id: 3, title: `${term} result ${Math.floor(Math.random() * 100)}` },
]
}
searchTerm = ''
results: Array<SearchResult> = []
handleSearch = async (term: string) => {
if (!term) {
this.results = []
return
}
const data = await this.fakeApi(term)
this.results = data
return data
}
asyncDebouncer = createAsyncDebouncer(
this,
this.handleSearch,
() => ({
key: 'createAsyncDebouncer',
wait: 500,
onError: (error) => {
console.error('Search failed:', error)
this.results = []
},
asyncRetryerOptions: {
maxAttempts: 3,
maxExecutionTime: 3000,
},
}),
(state) => state,
)
handleSearchDebounced = this.asyncDebouncer.maybeExecute
onSearchChange = async (e: Event) => {
const newTerm = (e.target as HTMLInputElement).value
this.searchTerm = newTerm
const result = await this.handleSearchDebounced(newTerm)
console.log('result', result)
}
override createRenderRoot() {
return this
}
override render() {
return html`<div>
<h1>TanStack Pacer createAsyncDebouncer Example</h1>
<div>
<input
autofocus
type="search"
.value=${this.searchTerm}
@input=${this.onSearchChange}
placeholder="Type to search..."
style="width: 100%"
autocomplete="new-password"
/>
</div>
<div style="margin-top: 10px">
<button @click=${() => this.asyncDebouncer.flush()}>Flush</button>
</div>
<div>
<p>API calls made: ${this.asyncDebouncer.state.successCount}</p>
${
this.results.length > 0
? html`<ul>
${this.results.map((item) => html`<li>${item.title}</li>`)}
</ul>`
: nothing
}${this.asyncDebouncer.state.isPending ? html`<p>Pending...</p>` : nothing}${this.asyncDebouncer.state.isExecuting ? html`<p>Executing...</p>` : nothing}
</div>
<pre style="margin-top: 20px">
${JSON.stringify(this.asyncDebouncer.state, null, 2)}</pre>
</div>`
}
}
customElements.define('pacer-demo', Demo)
class Example extends LitElement {
static properties = { mounted: { state: true } }
mounted = true
toggleMounted = (event: KeyboardEvent) => {
if (event.shiftKey && event.key === 'Enter') this.mounted = !this.mounted
}
private devtools?: TanStackDevtoolsCore
private target?: HTMLDivElement
override createRenderRoot() {
return this
}
override connectedCallback() {
super.connectedCallback()
document.addEventListener('keydown', this.toggleMounted)
if (!import.meta.env.DEV) return
this.target = document.createElement('div')
document.body.append(this.target)
this.devtools = new TanStackDevtoolsCore({
plugins: [pacerDevtoolsPlugin()],
})
this.devtools.mount(this.target)
}
override disconnectedCallback() {
this.devtools?.unmount()
this.target?.remove()
this.devtools = undefined
this.target = undefined
document.removeEventListener('keydown', this.toggleMounted)
super.disconnectedCallback()
}
override render() {
return html`${this.mounted ? html`<div><pacer-demo></pacer-demo></div>` : nothing}`
}
}
customElements.define('pacer-example', Example)
document.getElementById('app')!.append(document.createElement('pacer-example'))