Skip to content

Instantly share code, notes, and snippets.

@bt4R9
Last active August 12, 2024 14:55
Show Gist options
  • Save bt4R9/7027f6785388e8ad12c266bd16b28050 to your computer and use it in GitHub Desktop.
Save bt4R9/7027f6785388e8ad12c266bd16b28050 to your computer and use it in GitHub Desktop.
// QueryState Model
import {
type QueryClient,
type QueryFunction,
type QueryObserverOptions,
QueryObserver,
} from '@tanstack/query-core';
import {action, makeObservable, observable} from 'mobx';
export interface QueryStateParams<T> {
queryKey: string[];
queryClient: QueryClient;
queryFn: QueryFunction<T>;
refetchInterval: number | false;
staleTime: number;
throwOnError?: boolean;
}
export class QueryState<T> {
private queryKey: string[];
private queryFn: QueryFunction<T>;
private refetchInterval: number | false;
private staleTime: number;
private throwOnError?: boolean;
private queryClient: QueryClient;
private queryObserver: QueryObserver<T, Error>;
public data: T | undefined;
public isLoading: boolean;
public isRefetching: boolean;
public isError: boolean;
constructor(params: QueryStateParams<T>) {
this.queryKey = params.queryKey;
this.queryFn = params.queryFn;
this.refetchInterval = params.refetchInterval;
this.staleTime = params.staleTime;
this.throwOnError = params.throwOnError;
this.data = undefined;
this.isLoading = false;
this.isRefetching = false;
this.isError = false;
this.queryClient = params.queryClient;
this.queryObserver = new QueryObserver(
this.queryClient,
this.getObserverDefaultOptions({enabled: false}),
);
makeObservable(this, {
onQueryObserverResult: action.bound,
data: observable,
isLoading: observable,
isRefetching: observable,
isError: observable,
});
}
private getObserverDefaultOptions(
overrides: Partial<QueryObserverOptions<T, Error>> = {},
): QueryObserverOptions<T, Error> {
return {
queryKey: this.queryKey,
queryFn: this.queryFn,
refetchInterval: this.refetchInterval,
refetchOnWindowFocus: true,
refetchIntervalInBackground: false,
staleTime: this.staleTime,
throwOnError: this.throwOnError,
...overrides,
};
}
onQueryObserverResult() {
const response = this.queryObserver.getCurrentResult();
this.data = response.data;
this.isLoading = response.isLoading;
this.isRefetching = response.isRefetching;
this.isError = response.isError;
}
public setOptions(options: Partial<QueryObserverOptions<T, Error>> = {}) {
this.queryObserver.setOptions(this.getObserverDefaultOptions(options));
}
public init() {
const queryObserverUnsubscribe = this.queryObserver.subscribe(this.onQueryObserverResult);
return () => {
queryObserverUnsubscribe();
this.queryObserver.destroy();
};
}
}
// Hook
import {useEffect} from 'react';
interface ObjectWithInitMethod {
init(): () => unknown;
}
export const useQueryStateInitializer = (state: ObjectWithInitMethod) => {
useEffect(() => {
const disposer = state.init();
return () => {
disposer();
}
}, []);
};
// Usage
class MyModel {
constructor() {
this.queryState = new QueryState<MyAPIResponseType>({
queryClient: this.queryClient ?? new QueryClient(),
queryKey: ['my_key', 'some params'],
queryFn: ({signal}) => fetch('my-url', { signal });
refetchInterval: 30 * 1000,
staleTime: 60 * 1000,
});
this.queryState.data // is mobx reactive
}
init() {
this.queryState.setOptions({ enabled: true }); // or any other place to turn on the observer
const queryStateDisposer = this.queryState.init();
return () => {
queryStateDisposer();
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment