Docs
Query
Query Introduction

Query

With the power of createStores function, we can easily create a hook just like useQuery and useInfiniteQuery in TanStack-Query (opens in a new tab) using createQuery function.

It can dedupe multiple request, handle caching, auto-update stale data, handle retry on error, handle infinite query, and many more. With the flexibility given in createStores, we can extend its power according to our needs.

Query State & Network Fetching State

First of all, we need to understand the difference between these 2 types:

  • Query data state
  • Network fetching state

The query data state represented as status, isLoading, isSuccess, isError. These values has no relation with network fetching state. ⚠️

For network fetching state, we use isWaiting. The value will be true if the query is called and still waiting for the response. Here is the flow of both states:

Initial State
  • status: "loading"
  • isLoading: true
  • isSuccess: false
  • isError: false
  • isWaiting: false
Fetch data...
  • isWaiting: true
After succeed
  • status: "success"
  • isLoading: false
  • isSuccess: true
  • isError: false
  • isWaiting: false
Refetch...
  • isWaiting: true
After refetch failed
And no more retry
  • status: "success"
  • isLoading: false
  • isSuccess: true
  • isError: false
  • isWaiting: false
  • isRefetchError: true
  • isGoingToRetry: false
After failed
  • isWaiting: false
  • isGoingToRetry: bool
    (depends on option 👆)
Retry...
  • isWaiting: true
After failed
And no more retry
  • status: "error"
  • isLoading: false
  • isSuccess: false
  • isError: true
  • isWaiting: false
  • isGoingToRetry: false
💡

IMPORTANT NOTE:
By default, when isWaiting value is changed, the subscribers or the component which use the hook will not be notified. We can change this behavior by setting up the defaultDeps, or set it on specific component (in the dependency array).

Response Vs Data

const { isSuccess, data, response } = useMyQuery();

The response represents the untouched response returned from the async function. On the other hand, the data represents the customized selection of the response obtained using the select option. If we didn't define the select option, data will be equal to response.

Inherited from createStores

The createQuery function inherits functionality from the createStores function, allowing us to perform the same result and actions available in createStores.

const useMyQuery = createQuery(
  fetchMyData,
  {
    // 👇 Same as createStores options
    defaultDeps: undefined,
    onFirstSubscribe: (state) => console.log('onFirstSubscribe', state),
    onSubscribe: (state) => console.log('onSubscribe', state),
    onUnsubscribe: (state) => console.log('onUnsubscribe', state),
    onLastUnsubscribe: (state) => console.log('onLastUnsubscribe', state),
    onBeforeChangeKey: (nextKey, prevKey) => console.log('Store key changed', nextKey, prevKey),
 
    // ... other createQuery options (explained on next chapter)
  },
);

Custom reactivity (dependency array):

function QueryLoader() {
  // This component doesn't care whether the query is success or error.
  // It just listening to network fetching state. 👇
  const { isWaiting } = useMyQuery((state) => [state.isWaiting]);
  return <div>Is network fetching? {String(isWaiting)}</div>;
}

Actions outside component:

useMyQuery.get( /* ... */ );
useMyQuery.set( /* ... */ );
useMyQuery.subscribe( /* ... */ );
useMyQuery.getSubscribers( /* ... */ );

Fresh & Stale Query Concept

A query categorized as a fresh or stale depends on the comparison between the stale time config and the current time. For example, we use the default stale time, which is 3 seconds.

const useMyQuery = createQuery(fetchMyData);
const { fetch, forceFetch } = useMyQuery.get();
  • 07:00 AM – fetch the data
  • 07:01 AM – get the response
    (Since stale time is 3 seconds, the query will be categorized as fresh until 07:04 AM)
  • 07:02 AM to 07:04 AM 🟢 fresh
    • By default, when a component which use the hook is mounted, the query function will not get invoked.
    • By default, when a window focus event triggered, the query function will not get invoked.
    • Calling fetch() won't affect anything.
    • Calling forceFetch() will call the query function.
  • 07:05 AM onwards 🟠 stale
    • By default, when a component which use the hook is mounted, the query function will get invoked.
    • By default, when a window focus event triggered, the query function will get invoked.