TanStack

Lit Example: Playground

import { LitElement, html } from 'lit'
import { keyed } from 'lit/directives/keyed.js'
import { styleMap } from 'lit/directives/style-map.js'
import { QueryClient, QueryClientProvider } from '@tanstack/lit-query'
import './styles.css'
import { serverConfig } from './services/todos/api'
import './todo-list'
import './edit-todo'
import './add-todo'
import type { PropertyValues } from 'lit'

const queryClient = new QueryClient()

class PlaygroundQueryProvider extends QueryClientProvider {
  constructor() {
    super()
    this.client = queryClient
  }
}

class PlaygroundApp extends LitElement {
  static properties = {
    staleTime: { state: true },
    gcTime: { state: true },
    localErrorRate: { state: true },
    localFetchTimeMin: { state: true },
    localFetchTimeMax: { state: true },
    editingIndex: { state: true },
    views: { state: true },
  }

  private staleTime = 1000
  private gcTime = 3000
  private localErrorRate = serverConfig.errorRate
  private localFetchTimeMin = serverConfig.queryTimeMin
  private localFetchTimeMax = serverConfig.queryTimeMax
  private editingIndex: number | null = null
  private views = ['', 'fruit', 'grape']

  protected override createRenderRoot(): HTMLElement | DocumentFragment {
    return this
  }

  protected override willUpdate(changedProperties: PropertyValues) {
    serverConfig.errorRate = this.localErrorRate
    serverConfig.queryTimeMin = this.localFetchTimeMin
    serverConfig.queryTimeMax = this.localFetchTimeMax

    if (changedProperties.has('staleTime') || changedProperties.has('gcTime')) {
      queryClient.setDefaultOptions({
        queries: {
          staleTime: this.staleTime,
          gcTime: this.gcTime,
        },
      })
    }
  }

  private onNumberChange(event: Event, setValue: (value: number) => void) {
    const value = (event.currentTarget as HTMLInputElement).valueAsNumber
    if (!Number.isNaN(value)) {
      setValue(value)
    }
  }

  private setEditingIndex(event: CustomEvent<number | null>) {
    this.editingIndex = event.detail
  }

  render() {
    return html`
      <playground-query-provider>
        <p>
          The "staleTime" and "gcTime" durations have been altered in this
          example to show how query stale-ness and query caching work on a
          granular level
        </p>
        <div>
          Stale Time:
          <input
            type="number"
            min="0"
            step="1000"
            .value=${String(this.staleTime)}
            style=${styleMap({ width: '100px' })}
            @input=${(event: Event) =>
              this.onNumberChange(event, (value) => (this.staleTime = value))}
          />
        </div>
        <div>
          Garbage collection Time:
          <input
            type="number"
            min="0"
            step="1000"
            .value=${String(this.gcTime)}
            style=${styleMap({ width: '100px' })}
            @input=${(event: Event) =>
              this.onNumberChange(event, (value) => (this.gcTime = value))}
          />
        </div>
        <br />
        <div>
          Error Rate:
          <input
            type="number"
            min="0"
            max="1"
            step=".05"
            .value=${String(this.localErrorRate)}
            style=${styleMap({ width: '100px' })}
            @input=${(event: Event) =>
              this.onNumberChange(
                event,
                (value) => (this.localErrorRate = value),
              )}
          />
        </div>
        <div>
          Fetch Time Min:
          <input
            type="number"
            min="1"
            step="500"
            .value=${String(this.localFetchTimeMin)}
            style=${styleMap({ width: '60px' })}
            @input=${(event: Event) =>
              this.onNumberChange(
                event,
                (value) => (this.localFetchTimeMin = value),
              )}
          />
        </div>
        <div>
          Fetch Time Max:
          <input
            type="number"
            min="1"
            step="500"
            .value=${String(this.localFetchTimeMax)}
            style=${styleMap({ width: '60px' })}
            @input=${(event: Event) =>
              this.onNumberChange(
                event,
                (value) => (this.localFetchTimeMax = value),
              )}
          />
        </div>
        <br />
        <div class="App">
          <div>
            <button
              type="button"
              @click=${() => queryClient.invalidateQueries()}
            >
              Force Refetch All
            </button>
          </div>
          <br />
          <hr />
          ${this.views.map(
            (view) => html`
              <div>
                <todo-list
                  .initialFilter=${view}
                  @set-editing-index=${this.setEditingIndex}
                ></todo-list>
                <br />
              </div>
            `,
          )}
          <button
            type="button"
            @click=${() => (this.views = [...this.views, ''])}
          >
            Add Filter List
          </button>
          <hr />
          ${
            this.editingIndex !== null
              ? html`
                  ${keyed(
                    this.editingIndex,
                    html`<edit-todo
                      .editingIndex=${this.editingIndex}
                      @set-editing-index=${this.setEditingIndex}
                    ></edit-todo>`,
                  )}
                  <hr />
                `
              : null
          }
          <add-todo></add-todo>
        </div>
        <br />
      </playground-query-provider>
    `
  }
}

customElements.define('playground-query-provider', PlaygroundQueryProvider)
customElements.define('playground-app', PlaygroundApp)