import { QueryObserver } from '@tanstack/query-core'
import { createMemo } from 'solid-js'
import { useBaseQuery } from './useBaseQuery'
import type { DefaultError, QueryKey } from '@tanstack/query-core'
import type { QueryClient } from './QueryClient'
import type { Accessor } from 'solid-js'
import type {
DefinedUseQueryResult,
UseQueryOptions,
UseQueryResult,
} from './types'
import type {
DefinedInitialDataOptions,
UndefinedInitialDataOptions,
} from './queryOptions'
/**
* Subscribes to a query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* The query runs when the options call for it — `enabled: false` skips the initial fetch.
*
* This overload is selected when `initialData` is known to be defined, so the resulting `data` is never
* `undefined` (unless a `select` changes `TData` to include `undefined`).
* @param options - An accessor returning the {@link DefinedInitialDataOptions} to use — everything you can
* pass to `useQuery`, with `initialData` known to be defined.
* @param queryClient - An accessor for a custom `QueryClient`. Otherwise, the one from the nearest context
* will be used.
* @returns The current query result, as a Solid store, typed so that `status` is `success` — or `error` if a
* fetch attempt fails while keeping the existing data (`status` never resolves to `pending` in this overload's
* type, since `initialData` guarantees data upfront). `isSuccess`/`isError` are derived booleans for
* convenience.
* @see {@link queryOptions} to share these options between `useQuery` and imperative APIs like `queryClient.query`.
* @example
* ```tsx
* import { For } from 'solid-js'
* import { useQuery } from '@tanstack/solid-query'
*
* function Posts() {
* // `postsQuery.data` is never `undefined`, thanks to `initialData` — even if a refetch fails, so the
* // list stays visible alongside the error.
* const postsQuery = useQuery(() => ({
* queryKey: ['posts'],
* queryFn: fetchPosts,
* initialData: [],
* }))
*
* return (
*
* )
* }
* ```
*/
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: DefinedInitialDataOptions,
queryClient?: () => QueryClient,
): DefinedUseQueryResult
/**
* Subscribes to a query: a declarative dependency on an asynchronous source of data that is tied to a unique key.
* The query runs when the options call for it — `enabled: false` skips the initial fetch.
* @param options - An accessor returning the {@link UndefinedInitialDataOptions} to use — everything you can
* pass to `useQuery`.
* @param queryClient - An accessor for a custom `QueryClient`. Otherwise, the one from the nearest context
* will be used.
* @returns The current query result, as a Solid store. `status` is `pending` if there is no cached data to
* display, `error` if the last fetch attempt failed, or `success` if the query has data to display.
* `isPending`/`isSuccess`/`isError` are derived booleans for convenience.
* @see {@link queryOptions} to share these options between `useQuery` and imperative APIs like `queryClient.query`.
* @example
* ```tsx
* import { For, Match, Switch } from 'solid-js'
* import { useQuery } from '@tanstack/solid-query'
*
* function Posts() {
* const postsQuery = useQuery(() => ({
* queryKey: ['posts'],
* queryFn: fetchPosts,
* }))
*
* return (
*
* Loading...
* Error: {postsQuery.error.message}
*
*
*
*
* )
* }
* ```
* @example
* `select` derives whatever `data` a component needs from the cached value, without changing what's
* actually stored in the cache — the cache still holds the full `Post[]`, but `data` here is a `number`:
* ```tsx
* import { Match, Switch } from 'solid-js'
* import { useQuery } from '@tanstack/solid-query'
*
* function PostCount() {
* const postsQuery = useQuery(() => ({
* queryKey: ['posts'],
* queryFn: fetchPosts,
* select: (posts) => posts.length,
* }))
*
* return (
*
* Loading...
* Error: {postsQuery.error.message}
* {postsQuery.data} posts
*
* )
* }
* ```
* @example
* A dependent query, only enabled once `postId` is set:
* ```tsx
* import { Match, Switch } from 'solid-js'
* import { useQuery } from '@tanstack/solid-query'
*
* function Post(props: { postId: number | undefined }) {
* const postQuery = useQuery(() => ({
* queryKey: ['post', props.postId],
* queryFn: () => fetchPost(props.postId!),
* enabled: props.postId != null,
* }))
*
* return (
* {postQuery.data?.title}}>
* Select a post
* Loading...
* Error: {postQuery.error.message}
*
* )
* }
* ```
* @example
* The same dependent query, using `skipToken` to disable it in a type-safe way instead of relying on
* `enabled`. The non-null assertion is still needed — Solid's `props` narrowing doesn't survive into the
* `queryFn` closure the way a local `const` would — but `skipToken` keeps `queryFn`'s return type accurate
* without it. `refetch` doesn't work while `queryFn` is `skipToken` — use `enabled: false` instead if you
* need to trigger the query manually:
* ```tsx
* import { Match, Switch } from 'solid-js'
* import { skipToken, useQuery } from '@tanstack/solid-query'
*
* function Post(props: { postId: number | undefined }) {
* const postQuery = useQuery(() => ({
* queryKey: ['post', props.postId],
* queryFn: props.postId != null ? () => fetchPost(props.postId!) : skipToken,
* }))
*
* return (
* {postQuery.data?.title}}>
* Select a post
* Loading...
* Error: {postQuery.error.message}
*
* )
* }
* ```
* @example
* Seeding a detail query from an already-cached list, to skip the loading state. `initialDataUpdatedAt` carries
* over the list's own fetch time, so that if you set a `staleTime`, it's measured from when the list was
* fetched rather than from now:
* ```tsx
* import { useQuery, useQueryClient } from '@tanstack/solid-query'
*
* function Post(props: { postId: number }) {
* const queryClient = useQueryClient()
*
* const postQuery = useQuery(() => ({
* queryKey: ['post', props.postId],
* queryFn: () => fetchPost(props.postId),
* initialData: () =>
* queryClient
* .getQueryData>(['posts'])
* ?.find((post) => post.id === props.postId),
* initialDataUpdatedAt: () =>
* queryClient.getQueryState(['posts'])?.dataUpdatedAt,
* }))
*
* return postQuery.isError ? Error: {postQuery.error.message} :
{postQuery.data?.title}
* }
* ```
* @example
* Paginated data, keeping the previous page's data visible while the next page loads:
* ```tsx
* import { For, createSignal } from 'solid-js'
* import { keepPreviousData, useQuery } from '@tanstack/solid-query'
*
* function Posts() {
* const [page, setPage] = createSignal(0)
*
* const postsQuery = useQuery(() => ({
* queryKey: ['posts', page()],
* queryFn: () => fetchPosts(page()),
* placeholderData: keepPreviousData,
* }))
*
* return (
*