significant refactor

This commit is contained in:
2025-08-30 01:42:23 -05:00
parent 7136f646a3
commit 052f53444e
106 changed files with 1994 additions and 1701 deletions

View File

@@ -0,0 +1,4 @@
export * from './use-optimistic-mutation';
export * from './use-server-mutation';
export * from './use-server-query';
export * from './user-server-suspense-query';

View File

@@ -0,0 +1,37 @@
import { useQueryClient } from "@tanstack/react-query";
import { useServerMutation } from "./use-server-mutation";
export function useOptimisticMutation<TData, TVariables = unknown>(
options: Parameters<typeof useServerMutation<TData, TVariables>>[0] & {
queryKey: readonly (string | number)[];
optimisticUpdate?: (oldData: any, variables: TVariables) => any;
}
) {
const queryClient = useQueryClient();
const { queryKey, optimisticUpdate, ...mutationOptions } = options;
return useServerMutation({
...mutationOptions,
onMutate: async (variables) => {
await queryClient.cancelQueries({ queryKey });
const previousData = queryClient.getQueryData(queryKey);
if (optimisticUpdate && previousData) {
queryClient.setQueryData(queryKey, (old: any) => optimisticUpdate(old, variables));
}
return { previousData };
},
onError: (error, variables, context) => {
if (context && typeof context === 'object' && 'previousData' in context && context.previousData) {
queryClient.setQueryData(queryKey, context.previousData);
}
mutationOptions.onError?.(error, variables, context);
},
onSettled: (data, error, variables, context) => {
queryClient.invalidateQueries({ queryKey });
mutationOptions.onSettled?.(data, error, variables, context);
}
});
}

View File

@@ -0,0 +1,47 @@
import { useMutation, UseMutationOptions } from "@tanstack/react-query";
import { ServerResult } from "../types";
import toast from '@/lib/sonner'
export function useServerMutation<TData, TVariables = unknown>(
options: Omit<UseMutationOptions<TData, Error, TVariables>, 'mutationFn'> & {
mutationFn: (variables: TVariables) => Promise<ServerResult<TData>>;
successMessage?: string;
showErrorToast?: boolean;
showSuccessToast?: boolean;
}
) {
const {
mutationFn,
successMessage,
showErrorToast = true,
showSuccessToast = true,
onSuccess,
onError,
...mutationOptions
} = options;
return useMutation({
...mutationOptions,
mutationFn: async (variables: TVariables) => {
const result = await mutationFn(variables);
if (!result.success) {
if (showErrorToast) {
toast.error(result.error.userMessage);
}
throw new Error(result.error.userMessage);
}
return result.data;
},
onSuccess: (data, variables, context) => {
if (showSuccessToast && successMessage) {
toast.success(successMessage);
}
onSuccess?.(data, variables, context);
},
onError: (error, variables, context) => {
onError?.(error, variables, context);
}
});
}

View File

@@ -0,0 +1,31 @@
import { QueryKey, useQuery, UseQueryOptions } from "@tanstack/react-query";
import { ServerResult } from "../types";
import toast from '@/lib/sonner'
export function useServerQuery<TData>(
options: {
queryKey: QueryKey,
queryFn: () => Promise<ServerResult<TData>>;
options?: Omit<UseQueryOptions<TData, Error, TData>, 'queryFn' | 'queryKey'>
showErrorToast?: boolean;
}
) {
const { queryKey, queryFn, showErrorToast = true, options: queryOptions } = options;
return useQuery({
...queryOptions,
queryKey,
queryFn: async () => {
const result = await queryFn();
if (!result.success) {
if (showErrorToast) {
toast.error(result.error.userMessage);
}
throw new Error(result.error.userMessage);
}
return result.data;
}
});
}

View File

@@ -1,132 +0,0 @@
import { useQuery, useMutation, useQueryClient, UseQueryOptions, UseMutationOptions, useSuspenseQuery } from '@tanstack/react-query';
import { toast } from 'sonner';
import { ServerResult } from '@/lib/tanstack-query/types';
export function useServerQuery<TData>(
options: Omit<UseQueryOptions<TData, Error, TData>, 'queryFn'> & {
queryFn: () => Promise<ServerResult<TData>>;
showErrorToast?: boolean;
}
) {
const { queryFn, showErrorToast = true, ...queryOptions } = options;
return useQuery({
...queryOptions,
queryFn: async () => {
const result = await queryFn();
if (!result.success) {
if (showErrorToast) {
toast.error(result.error.userMessage);
}
throw new Error(result.error.userMessage);
}
return result.data;
}
});
}
export function useServerSuspenseQuery<TData>(
options: Omit<UseQueryOptions<TData, Error, TData>, 'queryFn'> & {
queryFn: () => Promise<ServerResult<TData>>;
showErrorToast?: boolean;
}
) {
const { queryFn, showErrorToast = true, ...queryOptions } = options;
return useSuspenseQuery({
...queryOptions,
queryFn: async () => {
const result = await queryFn();
if (!result.success) {
if (showErrorToast) {
toast.error(result.error.userMessage);
}
throw new Error(result.error.userMessage);
}
return result.data;
}
});
}
export function useServerMutation<TData, TVariables = unknown>(
options: Omit<UseMutationOptions<TData, Error, TVariables>, 'mutationFn'> & {
mutationFn: (variables: TVariables) => Promise<ServerResult<TData>>;
successMessage?: string;
showErrorToast?: boolean;
showSuccessToast?: boolean;
}
) {
const {
mutationFn,
successMessage,
showErrorToast = true,
showSuccessToast = true,
onSuccess,
onError,
...mutationOptions
} = options;
return useMutation({
...mutationOptions,
mutationFn: async (variables: TVariables) => {
const result = await mutationFn(variables);
if (!result.success) {
if (showErrorToast) {
toast.error(result.error.userMessage);
}
throw new Error(result.error.userMessage);
}
return result.data;
},
onSuccess: (data, variables, context) => {
if (showSuccessToast && successMessage) {
toast.success(successMessage);
}
onSuccess?.(data, variables, context);
},
onError: (error, variables, context) => {
onError?.(error, variables, context);
}
});
}
export function useOptimisticMutation<TData, TVariables = unknown>(
options: Parameters<typeof useServerMutation<TData, TVariables>>[0] & {
queryKey: readonly (string | number)[];
optimisticUpdate?: (oldData: any, variables: TVariables) => any;
}
) {
const queryClient = useQueryClient();
const { queryKey, optimisticUpdate, ...mutationOptions } = options;
return useServerMutation({
...mutationOptions,
onMutate: async (variables) => {
await queryClient.cancelQueries({ queryKey });
const previousData = queryClient.getQueryData(queryKey);
if (optimisticUpdate && previousData) {
queryClient.setQueryData(queryKey, (old: any) => optimisticUpdate(old, variables));
}
return { previousData };
},
onError: (error, variables, context) => {
if (context && typeof context === 'object' && 'previousData' in context && context.previousData) {
queryClient.setQueryData(queryKey, context.previousData);
}
mutationOptions.onError?.(error, variables, context);
},
onSettled: (data, error, variables, context) => {
queryClient.invalidateQueries({ queryKey });
mutationOptions.onSettled?.(data, error, variables, context);
}
});
}

View File

@@ -0,0 +1,32 @@
import { QueryKey, UseQueryOptions, useSuspenseQuery } from "@tanstack/react-query";
import { ServerResult } from "../types";
import toast from '@/lib/sonner'
export function useServerSuspenseQuery<TData>(
options: {
queryKey: QueryKey,
queryFn: () => Promise<ServerResult<TData>>;
options?: Omit<UseQueryOptions<TData, Error, TData>, 'queryFn' | 'queryKey'>
showErrorToast?: boolean;
}
) {
const { queryKey, queryFn, showErrorToast = true, options: queryOptions } = options;
const queryResult = useSuspenseQuery({
...queryOptions,
queryKey,
queryFn: async () => {
const result = await queryFn();
if (!result.success) {
if (showErrorToast) {
toast.error(result.error.userMessage);
}
throw new Error(result.error.userMessage);
}
return result.data;
}
});
return queryResult;
}