query()

Declares a client-side data fetching field inside a model.

import { query } from 'comwit'

Basic usage

import { model, query } from 'comwit'

export const post = model<PostState>({
  posts: query<Post[]>({
    initialData: [],
    queryFn: () => api.post.findAll(),
  }),
})

The type in your state should be Query<Data> (or Query<Data, Arg> if the queryFn takes an argument):

import { Query } from 'comwit'

export type PostState = {
  posts: Query<Post[]>
  comments: Query<Comment[], string> // string = queryFn arg type
}

Methods

Once accessed via state() in an action, query fields expose these methods:

MethodDescription
.query(arg?)Fetch data. Respects staleTime — skips if data is fresh.
.refetch()Force re-fetch with the last used argument.
.set(data)Manually set data (for optimistic updates).

Status flags

FlagTypeDescription
.dataTThe current data
.isLoadingbooleanTrue on first fetch (no prior success/error)
.isFetchingbooleanTrue during any fetch
.isSuccessbooleanTrue after a successful fetch
.isErrorbooleanTrue after a failed fetch
.errorstring | nullError message if failed

Options

Pass options at the query definition or globally via MuchaProvider:

OptionDescription
staleTimeTime in ms before cached data is considered stale (default: 0)
cacheTimeAlias for gcTime
gcTimeTime in ms before unused cache entries are garbage collected
placeholderDataData to show while loading. Use keepPreviousData to retain previous results.
forceSkip cache and always fetch (call-time only)

queryFn context

queryFn receives (arg, context) where context.state is a readonly snapshot of the current query state:

posts: query<Post[]>({
  initialData: [],
  queryFn: (_, { state }) => {
    // state.data, state.isLoading, etc.
    return api.post.findAll()
  },
})

query.infinite()

For infinite scroll / pagination. Use Query.Infinite<T> as the type.

import { Query } from 'comwit'

// types.ts
export type PostState = {
  trending: Query.Infinite<Post[]>
}
// model.ts
import { query } from 'comwit'

export const post = model<PostState>({
  trending: query.infinite<Post[]>({
    initialData: [],
    queryFn: (_, { state }) => api.post.trending(state.cursor),
  }),
})

Additional methods

MethodDescription
.nextFetch()Fetch the next page. No-op when hasMore is false.
.previousFetch()Go back to the previous cursor.

Additional flags

FlagTypeDescription
.cursorstring | nullCurrent pagination cursor
.hasMorebooleanWhether more pages exist

Usage in actions

async loadMoreTrending() {
  await this.model.trending.nextFetch()
}

The queryFn return value can include cursor and hasMore to control pagination:

queryFn: async (_, { state }) => {
  const res = await api.post.trending(state.cursor)
  return {
    data: res.posts,
    cursor: res.nextCursor,
    hasMore: res.hasNext,
  }
}