Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc changes to error handling and useMergedInfiniteQueries #757

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { act } from 'react-dom/test-utils';
import { renderHook, waitFor } from '@/test-utils/rtl';

import useMergedInfiniteQueries from '../use-merged-infinite-queries';
import { UseMergedInfiniteQueriesError } from '../use-merged-infinite-queries-error';
import { type SingleInfiniteQueryOptions } from '../use-merged-infinite-queries.types';

type MockAPIResponse = {
Expand Down Expand Up @@ -105,6 +106,7 @@ describe(useMergedInfiniteQueries.name, () => {
const [mergedResult] = result.current;
expect(mergedResult.data).toStrictEqual([0, 2, 4, 6, 8]);
expect(mergedResult.status).toStrictEqual('error');
expect(mergedResult.error).toBeInstanceOf(UseMergedInfiniteQueriesError);
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class UseMergedInfiniteQueriesError extends Error {
errors: Array<Error>;
constructor(message: string, errors: Array<Error>, options?: ErrorOptions) {
super(message, options);
this.errors = errors;
this.name = 'UseMergedInfiniteQueriesError';
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useMemo, useState, useEffect } from 'react';
import { useMemo, useState, useEffect, useCallback } from 'react';

import { InfiniteQueryObserver, useQueryClient } from '@tanstack/react-query';

import mergeSortedArrays from '@/utils/merge-sorted-arrays';

import getMergedFetchNextPage from './helpers/get-merged-fetch-next-page';
import getMergedQueryStatus from './helpers/get-merged-query-status';
import { UseMergedInfiniteQueriesError } from './use-merged-infinite-queries-error';
import {
type SingleInfiniteQueryResult,
type MergedQueriesResults,
Expand All @@ -26,8 +27,8 @@ import {
* @param flattenResult - A function that takes the expected query result and flattens it into an array of items
* @param compare - A comparison function used to sort and merge results.
* The function should accept two arguments and return:
* - A number > 0 if the first argument has a higher priority.
* - A number <= 0 if the second argument has a higher or equal priority.
* - A number > 0 if the second argument comes first.
* - A number <= 0 if the first argument comes first.
* - **Note:** The comparison logic must match the sorting logic used in the queries to maintain consistency.
*
* @returns A tuple [mergedQueryResults, queryResults]:
Expand Down Expand Up @@ -85,6 +86,14 @@ export default function useMergedInfiniteQueries<TData, TResponse, TPageParam>({
});
}, [flattenedDataArrays, count, compare]);

const refetchQueriesWithError = useCallback(() => {
queryResults.forEach((res) => {
if (res.isError) {
res.refetch();
}
});
}, [queryResults]);

const mergedQueryResults = {
data: sortedArray,
status: getMergedQueryStatus(queryResults),
Expand All @@ -99,6 +108,18 @@ export default function useMergedInfiniteQueries<TData, TResponse, TPageParam>({
pageSize,
setCount,
}),
error: queryResults.some((qr) => qr.isError)
? new UseMergedInfiniteQueriesError(
'One or more infinite queries failed',
queryResults.reduce((errors: Array<Error>, qr) => {
if (qr.isError) {
errors.push(qr.error);
}
return errors;
}, [])
)
: null,
refetch: refetchQueriesWithError,
// ...add other properties if needed
};
return [mergedQueryResults, queryResults];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
type useInfiniteQuery,
} from '@tanstack/react-query';

import { type UseMergedInfiniteQueriesError } from './use-merged-infinite-queries-error';

export type MergedQueryStatus = 'idle' | 'loading' | 'success' | 'error';

export type MergedQueriesResults<TData> = {
Expand All @@ -15,6 +17,8 @@ export type MergedQueriesResults<TData> = {
isFetchingNextPage: boolean;
hasNextPage: boolean;
fetchNextPage: () => void;
error: UseMergedInfiniteQueriesError | null;
refetch: () => void;
};

export type SingleInfiniteQueryOptions<TResponse, TPageParam> =
Expand Down
13 changes: 12 additions & 1 deletion src/utils/request/request-error.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { type ZodIssue } from 'zod';

export class RequestError extends Error {
status: number;
constructor(message: string, status: number, options?: ErrorOptions) {
validationErrors: Array<ZodIssue> | undefined;
constructor(
message: string,
status: number,
validationErrors?: Array<ZodIssue>,
options?: ErrorOptions
) {
super(message, options);
this.status = status;
if (validationErrors?.length) {
this.validationErrors = validationErrors;
}
this.name = 'RequestError';
}
}
11 changes: 8 additions & 3 deletions src/utils/request/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ export default function request(
async (res) => {
if (!res.ok) {
const error = await res.json();
throw new RequestError(error.message, res.status, {
cause: error.cause,
});
throw new RequestError(
error.message,
res.status,
error.validationErrors,
{
cause: error.cause,
}
);
}
return res;
}
Expand Down
Loading