diff --git a/.changeset/real-boxes-admire.md b/.changeset/real-boxes-admire.md new file mode 100644 index 00000000..73872780 --- /dev/null +++ b/.changeset/real-boxes-admire.md @@ -0,0 +1,5 @@ +--- +"@reactive-dot/react": minor +--- + +BREAKING: The query error resetter no longer accepts a specific error input; it now defaults to resetting all errors globally. diff --git a/apps/docs/docs/getting-started/query.md b/apps/docs/docs/getting-started/query.md index 74df4946..272fe79c 100644 --- a/apps/docs/docs/getting-started/query.md +++ b/apps/docs/docs/getting-started/query.md @@ -203,7 +203,7 @@ function QueryWithRefresh() { ## Retry failed query -Error from queries can be caught and reset using `ErrorBoundary` & [`useQueryErrorResetter`](/api/react/function/useQueryErrorResetter) hook. +Error from queries can be reset using `ErrorBoundary` & [`useQueryErrorResetter`](/api/react/function/useQueryErrorResetter) hook. ```tsx import { useQueryErrorResetter } from "@reactive-dot/react"; @@ -226,12 +226,7 @@ function AppErrorBoundary() { return ( { - if (details.reason === "imperative-api") { - const [error] = details.args; - resetQueryError(error); - } - }} + onReset={() => resetQueryError()} > {/* ... */} diff --git a/examples/react/src/app.tsx b/examples/react/src/app.tsx index 2b88d7c1..59721474 100644 --- a/examples/react/src/app.tsx +++ b/examples/react/src/app.tsx @@ -68,12 +68,7 @@ function Example({ chainName }: ExampleProps) {
{ - if (details.reason === "imperative-api") { - const [error] = details.args; - resetQueryError(error); - } - }} + onReset={() => resetQueryError()} > Loading {chainName}...}>

{chainName}

@@ -85,13 +80,13 @@ function Example({ chainName }: ExampleProps) { ); } -function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) { +function ErrorFallback({ resetErrorBoundary }: FallbackProps) { return (
Oops, something went wrong!
-
diff --git a/packages/react/src/hooks/use-query-error-resetter.ts b/packages/react/src/hooks/use-query-error-resetter.ts index 10375666..d3b481d5 100644 --- a/packages/react/src/hooks/use-query-error-resetter.ts +++ b/packages/react/src/hooks/use-query-error-resetter.ts @@ -1,11 +1,21 @@ -import { resetQueryError } from "../utils/jotai.js"; +import { atomFamilyErrorsAtom } from "../utils/jotai.js"; +import { useAtomCallback } from "jotai/utils"; +import { useCallback } from "react"; /** * Hook for getting function to reset query error caught by error boundary * * @returns Function to reset caught query error */ -// eslint-disable-next-line @eslint-react/hooks-extra/no-redundant-custom-hook export function useQueryErrorResetter() { - return resetQueryError; + return useAtomCallback( + useCallback((get) => { + const atomFamilyErrors = get(atomFamilyErrorsAtom); + + for (const error of atomFamilyErrors) { + error.atomFamily.remove(error.param); + atomFamilyErrors.delete(error); + } + }, []), + ); } diff --git a/packages/react/src/utils/jotai.ts b/packages/react/src/utils/jotai.ts index d7a8dc59..e8e3087d 100644 --- a/packages/react/src/utils/jotai.ts +++ b/packages/react/src/utils/jotai.ts @@ -1,34 +1,17 @@ -import { QueryError } from "@reactive-dot/core"; -import type { Atom, Getter } from "jotai"; +import { atom, type Atom, type Getter } from "jotai"; import { atomFamily } from "jotai/utils"; import type { AtomFamily } from "jotai/vanilla/utils/atomFamily"; import { Observable } from "rxjs"; import { catchError } from "rxjs/operators"; -export class AtomFamilyError extends QueryError { - constructor( - readonly atomFamily: AtomFamily, - readonly param: unknown, - message: string | undefined, - options?: ErrorOptions, - ) { - super(message, options); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - static fromAtomFamilyError>( - error: TError, - atomFamily: TAtomFamily, - param: TAtomFamily extends AtomFamily - ? Param - : unknown, - message?: string, - ) { - return new this(atomFamily, param, message, { - cause: error, - }); - } -} +export const atomFamilyErrorsAtom = atom( + () => + new Set<{ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + atomFamily: AtomFamily; + param: unknown; + }>(), +); export function atomFamilyWithErrorCatcher< TParam, @@ -55,38 +38,35 @@ export function atomFamilyWithErrorCatcher< const atomCatching: TAtomCreator = (read, ...args) => { // @ts-expect-error complex sub-type const readCatching: TRead = (...readArgs) => { + const addError = (error: T) => { + const get = readArgs[0] as Getter; + get(atomFamilyErrorsAtom).add({ + atomFamily: baseAtomFamily, + param, + }); + return error; + }; + try { const value = read(...readArgs); if (value instanceof Promise) { return value.catch((error) => { - throw AtomFamilyError.fromAtomFamilyError( - error, - baseAtomFamily, - param, - ); + throw addError(error); }); } if (value instanceof Observable) { return value.pipe( catchError((error) => { - throw AtomFamilyError.fromAtomFamilyError( - error, - baseAtomFamily, - param, - ); + throw addError(error); }), ); } return value; } catch (error) { - throw AtomFamilyError.fromAtomFamilyError( - error, - baseAtomFamily, - param, - ); + throw addError(error); } }; @@ -101,17 +81,3 @@ export function atomFamilyWithErrorCatcher< return baseAtomFamily; } - -export function resetQueryError(error: unknown) { - if (!(error instanceof Error)) { - return; - } - - if (error instanceof AtomFamilyError) { - error.atomFamily.remove(error.param); - } - - if (error.cause instanceof Error) { - resetQueryError(error.cause); - } -}