Query Docs
Examples
- Simple
- Basic
- Basic w/ GraphQL-Request
- Default Query Function
- Solid Start
- Astro
- Offline Queries and Mutations
- Playground
- Algolia
- Chat example (streaming)
- Shadow DOM
- Devtools Embedded Panel
- Star Wars
- Rick And Morty
- Solid Router
- Suspense
- Batching
- Auto Refetching / Polling / Realtime
- Optimistic Updates
- Pagination
- Prefetching
Solid Example: Playground
/* @refresh reload */
import {
For,
Index,
Match,
Show,
Switch,
createEffect,
createMemo,
createSignal,
} from 'solid-js'
import { render } from 'solid-js/web'
import {
QueryClient,
QueryClientProvider,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/solid-query'
import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
import { serverConfig } from './services/todos/api'
import { todosMutations, todosQueries } from './services/todos/queries'
import type { Todo } from './services/todos/types'
import type { JSX } from 'solid-js'
import './styles.css'
const initialStaleTime = 1000
const initialGcTime = 3000
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: initialStaleTime,
gcTime: initialGcTime,
},
},
})
function onNumberInput(
setValue: (value: number) => void,
): JSX.EventHandler<HTMLInputElement, InputEvent> {
return (event) => {
const value = event.currentTarget.valueAsNumber
if (!Number.isNaN(value)) {
setValue(value)
}
}
}
function Root() {
const [staleTime, setStaleTime] = createSignal(initialStaleTime)
const [gcTime, setGcTime] = createSignal(initialGcTime)
const [localErrorRate, setLocalErrorRate] = createSignal(
serverConfig.errorRate,
)
const [localFetchTimeMin, setLocalFetchTimeMin] = createSignal(
serverConfig.queryTimeMin,
)
const [localFetchTimeMax, setLocalFetchTimeMax] = createSignal(
serverConfig.queryTimeMax,
)
createEffect(() => {
serverConfig.errorRate = localErrorRate()
serverConfig.queryTimeMin = localFetchTimeMin()
serverConfig.queryTimeMax = localFetchTimeMax()
})
createEffect(() => {
queryClient.setDefaultOptions({
queries: {
staleTime: staleTime(),
gcTime: gcTime(),
},
})
})
return (
<QueryClientProvider client={queryClient}>
<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={staleTime()}
onInput={onNumberInput(setStaleTime)}
style={{ width: '100px' }}
/>
</div>
<div>
Garbage collection Time:{' '}
<input
type="number"
min="0"
step="1000"
value={gcTime()}
onInput={onNumberInput(setGcTime)}
style={{ width: '100px' }}
/>
</div>
<br />
<div>
Error Rate:{' '}
<input
type="number"
min="0"
max="1"
step=".05"
value={localErrorRate()}
onInput={onNumberInput(setLocalErrorRate)}
style={{ width: '100px' }}
/>
</div>
<div>
Fetch Time Min:{' '}
<input
type="number"
min="1"
step="500"
value={localFetchTimeMin()}
onInput={onNumberInput(setLocalFetchTimeMin)}
style={{ width: '60px' }}
/>{' '}
</div>
<div>
Fetch Time Max:{' '}
<input
type="number"
min="1"
step="500"
value={localFetchTimeMax()}
onInput={onNumberInput(setLocalFetchTimeMax)}
style={{ width: '60px' }}
/>
</div>
<br />
<App />
<br />
<SolidQueryDevtools initialIsOpen />
</QueryClientProvider>
)
}
function App() {
const queryClient = useQueryClient()
const [editingIndex, setEditingIndex] = createSignal<number | null>(null)
const [views, setViews] = createSignal(['', 'fruit', 'grape'])
const editing = createMemo(() => {
const index = editingIndex()
return index === null ? undefined : { index }
})
return (
<div class="App">
<div>
<button onClick={() => queryClient.invalidateQueries()}>
Force Refetch All
</button>
</div>
<br />
<hr />
<Index each={views()}>
{(view) => (
<div>
<Todos initialFilter={view()} setEditingIndex={setEditingIndex} />
<br />
</div>
)}
</Index>
<button
onClick={() => {
setViews((old) => [...old, ''])
}}
>
Add Filter List
</button>
<hr />
<Show when={editing()} keyed>
{({ index }) => (
<>
<EditTodo editingIndex={index} setEditingIndex={setEditingIndex} />
<hr />
</>
)}
</Show>
<AddTodo />
</div>
)
}
function Todos(props: {
initialFilter: string
setEditingIndex: (editingIndex: number | null) => void
}) {
const [filter, setFilter] = createSignal(props.initialFilter)
const query = useQuery(() => todosQueries.list(filter()))
return (
<div>
<div>
<label>
Filter:{' '}
<input
value={filter()}
onInput={(e) => setFilter(e.currentTarget.value)}
/>
</label>
</div>
<Switch>
<Match when={query.status === 'pending'}>
<span>Loading... (Attempt: {query.failureCount + 1})</span>
</Match>
<Match when={query.status === 'error'}>
<span>
Error: {query.error?.message}
<br />
<button onClick={() => query.refetch()}>Retry</button>
</span>
</Match>
<Match when={query.data}>
{(data) => (
<>
<ul>
<For each={data()}>
{(todo) => (
<li>
{todo.name}{' '}
<button onClick={() => props.setEditingIndex(todo.id)}>
Edit
</button>
</li>
)}
</For>
</ul>
<div>
<Show when={query.isFetching} fallback={<span> </span>}>
<span>
Background Refreshing... (Attempt: {query.failureCount + 1})
</span>
</Show>
</div>
</>
)}
</Match>
</Switch>
</div>
)
}
function EditTodo(props: {
editingIndex: number
setEditingIndex: (editingIndex: number | null) => void
}) {
const queryClient = useQueryClient()
const query = useQuery(() => todosQueries.detail(props.editingIndex))
const [draft, setDraft] = createSignal<Partial<Omit<Todo, 'id'>>>({})
const saveMutation = useMutation(() => ({
...todosMutations.update(),
onSuccess: (todo) => {
// Update `todos` and the individual todo queries when this mutation succeeds
queryClient.invalidateQueries({ queryKey: todosQueries.lists() })
queryClient.setQueryData(
todosQueries.detail(props.editingIndex).queryKey,
todo,
)
setDraft({})
},
}))
const disableEditSave = () =>
query.status === 'pending' || saveMutation.status === 'pending'
return (
<div>
<div>
<Show when={query.data}>
{(data) => (
<>
<button onClick={() => props.setEditingIndex(null)}>Back</button>{' '}
Editing Todo "{data().name}" (#
{props.editingIndex})
</>
)}
</Show>
</div>
<Switch>
<Match when={query.status === 'pending'}>
<span>Loading... (Attempt: {query.failureCount + 1})</span>
</Match>
<Match when={query.status === 'error'}>
<span>
Error! <button onClick={() => query.refetch()}>Retry</button>
</span>
</Match>
<Match when={query.data}>
{(data) => (
<>
<label>
Name:{' '}
<input
value={draft().name ?? data().name}
onInput={(e) =>
setDraft((old) => ({
...old,
name: e.currentTarget.value,
}))
}
disabled={disableEditSave()}
/>
</label>
<label>
Notes:{' '}
<input
value={draft().notes ?? data().notes}
onInput={(e) =>
setDraft((old) => ({
...old,
notes: e.currentTarget.value,
}))
}
disabled={disableEditSave()}
/>
</label>
<div>
<button
onClick={() => saveMutation.mutate({ ...data(), ...draft() })}
disabled={disableEditSave()}
>
Save
</button>
</div>
<div>
<Switch>
<Match when={saveMutation.status === 'pending'}>
Saving...
</Match>
<Match when={saveMutation.status === 'error'}>
{saveMutation.error?.message}
</Match>
<Match when={saveMutation.status === 'success'}>Saved!</Match>
</Switch>
</div>
<div>
<Show when={query.isFetching} fallback={<span> </span>}>
<span>
Background Refreshing... (Attempt: {query.failureCount + 1})
</span>
</Show>
</div>
</>
)}
</Match>
</Switch>
</div>
)
}
function AddTodo() {
const queryClient = useQueryClient()
const [name, setName] = createSignal('')
const addMutation = useMutation(() => ({
...todosMutations.create(),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: todosQueries.lists() })
},
}))
return (
<div>
<input
value={name()}
onInput={(e) => setName(e.currentTarget.value)}
disabled={addMutation.status === 'pending'}
/>
<button
onClick={() => {
addMutation.mutate({ name: name(), notes: 'These are some notes' })
}}
disabled={addMutation.status === 'pending' || !name()}
>
Add Todo
</button>
<div>
<Switch>
<Match when={addMutation.status === 'pending'}>Saving...</Match>
<Match when={addMutation.status === 'error'}>
{addMutation.error?.message}
</Match>
<Match when={addMutation.status === 'success'}>Saved!</Match>
</Switch>
</div>
</div>
)
}
const rootElement = document.getElementById('root')
if (!rootElement) throw new Error('Missing #root element')
render(() => <Root />, rootElement)