What's the type of React Query's Error and how to handle different cases? - reactjs

I'm using React Query with typescript to fetch data in my project and I'm trying to use the error the useQuery hook returns to display a message describing the error if it exists like this :
{isError && (error)? <p className=" text-[#ff0000] text-center font-semibold">{error?.response.data.message}</p> : null}
I'm having a problem with the error type :
How can I fix it, I couldn't find anything on the internet and if possible can anyone explain how to handle different thrown error with react query since you can throw anything in JS/TS

error defaults to type unknown because your queryFn can throw anything, that's a javascript behaviour. throw 5 will give you number as your error type. There is no good way to assert that, so unknown is the best possible type. It's also what TypeScript does now per default since v4.4 (useUnknownInCatchVariables)
The best way to make sure that your app runs as you want is to narrow the type at runtime. if (error instanceof Error) does the trick, and the you can safely access error.message.
If you are using axios, the type is actually an AxiosError, and axios has an axios.isAxiosError(error) helper that checks for that and also narrows the type correctly.
The alternative is to provide generics to useQuery:
useQuery<Todo[], AxiosError>(['todos'], fetchTodos)
but this is bad for a couple of reasons:
there are actually four generics, and you remove a bunch of features by only providing two of them
There is no guarantee that your error will really be an axios error. For example, if you use select, and a runtime error happens in there (because of some faulty logic), your error will be a "normal" Error, but not an axios Error.
Alas, as you can throw anything in JS, unknown is the most correct type.
I'm also going into more details in my blog:
https://tkdodo.eu/blog/react-query-and-type-script

As #TkDodo has pointed out, you could provide generics to useQuery but it's not worth it, since you will lose the Type inference too.
However, as a workaround, I add the onError callback and type its error arg instead. TypeScript will infer the type of that error to the type I am expecting from useQuery.
Note that I am using Axios request & response interceptors for all requests that I use to format and throw my custom errors.
Example...
interface IPayload {
someKey: string; // ETC
}
// The type of error expected from the response (also formatted by interceptor).
interface IApiError {
message: string;
description: string;
statusCode: string | number;
}
export const useMyQuery = (payload: IPayload) => {
const { data, isLoading, isError, error, refetch } = useQuery({
queryKey: ['some', 'query-keys'],
queryFn: () => API.fetchMyData(payload),
// This does the trick
onError: (err: IApiError) => err,
});
};

I had same issue with Typescript and react-query, same error Object is of type 'unknown'.
Installing this devDependency "#types/react-query" helped me somehow. I am using VS Code editor and I think that helped with type suggestions. This might help.
npm i --save-dev #types/react-query

For Mutation only:
If you are using Axios for Api calls, use the following to get Axios type errors:
const error = mutation.error as AxiosError;
Now, the error object will not throw any object not found warning.

Related

React Redux: How to handle errors in RTK Queries/Mutation Typescript?

Hope your all are doing fine.
Im using Typescript with RTK mutation everything is working good but if i send any error from backend in specific json format like
{
status: "Error",
message "Something went wrong"
}
when i check on my browser network tab its showing me the correct error response like:
{
data: {
status: "Error",
message "Something went wrong"
}
}
Im getting error in the mutation hook:
const [createCategory, {isLoading, error }] = useCreateCategoryMutation();
but I can't access error.data.message in my react it is giving me types error like:
Property 'data' does not exist on type 'FetchBaseQueryError | SerializedError'.
At this point, it could be an error from the server (FetchBaseQueryError) or just any error thrown by code you wrote (SerializedError, e.g. in query, queryFn, transformResponse etc.) - and that could have a completely different shape.
To make sure it's a FetchBaseQueryError, just do
if ('data' in error) {
// TypeScript will handle it as `FetchBaseQueryError` from now on.
}
I found the answer for your question here written by Phry as well :) ,
https://github.com/rtk-incubator/rtk-query/issues/86#issuecomment-738845312
If you know the format that will be returned with all non-2xx-status responses from your server, you could just cast your
fetchQuery as BaseQueryFn<string | FetchArgs, unknown, YourErrorType, {}>.

Typescript type declarations for Promise that returns a React Component

I have the following function:
const withCacheRefresh = (lazyLoadComponent: any) => {
return new Promise<React.ComponentType<any>>((resolve) => {
lazyLoadComponent()
.then(resolve)
.catch(() => {
window.location.reload(true);
});
});
}
It takes in a promise as an argument, specifically lazy. I proceed to call the promise, either resolving the promise by returning the result of lazy, or in my catch, refresh the page. I'm running into issues with the following Typescript compilation error:
Type 'Promise<ComponentType<any>>' is not assignable to type 'Promise<{ default: ComponentType<any>; }>'.
Type 'ComponentType<any>' is not assignable to type '{ default: ComponentType<any>; }'.
Property 'default' is missing in type 'ComponentClass<any, any>' but required in type '{ default: ComponentType<any>; }'. TS2322
I have two questions:
How can I specify my first argument lazyLoadComponent to be of the type for a function that returns a component of React.Component<any> type instead of just any?
How can I fix my Promise<React.ComponentType<any>> definition such that it conforms to the required type of Promise<{ default: ComponentType<any>; }> ?
Update
I've received answers telling me that my solution is incorrect for my stated problem, but I purposely chose to omit details about the nuances of my larger problem space.
Since it was asked, I feel like there's no pain clarifying: when new deployments get released for my application, new chunks get created, and the old ones purged from the production workspace. Consequently, clients that still remain in the previous release (since the app hasn't been refreshed to the latest version) are still using old chunks. HMR would not work since this is a production environment. An explicit refresh would cause the service workers to retrieve the latest chunks.
A similar circumstance is described in this Github Issue.
This is a convoluted (and slightly incorrect) way of doing things.
I'm going to assume you have a reason for reloading the page on an error other than "I hope it fixes the error" (if that's the reason or if it's for cache-busting purposes, you should definitely try HMR).
EDIT Since OP has clarified what he meant, I'd suggest, instead of hoping the browser doesn't cache the lazy-loaded component and thereby errors when a new version is rolled out, to use a precache manifest in the Service Worker itself, which would allow the SW to load the manifest whenever a new one is regestired. See this question for more info. You can integrate it into the bundler itself with the Workbox plugin, even if you don't use Workbox.
I'm also going to assume you want to use this with lazy, not call it on lazy, because otherwise it's not exactly possible to catch any errors.
import React from 'react';
type LazyFactory = Parameters<typeof React.lazy>[0];
const withCacheRefresh = (
importResult: LazyFactory
): LazyFactory => () =>
importResult().catch(() => {
window.location.reload(true);
// Following makes return type `never`, which satisfies TypeScript
throw new Error('component load failed');
});
There's no need to wrap it in a promise because it already is one. You can just .catch on it directly.
Usage:
const MyLazyComponent = React.lazy(withCacheRefresh(() => import('./Component')));

Apollo client not paring UserInputError

i am throwing an UserInputError from the Apollo server, the playground is correctly displaying the error with extension and exceptions, but the the apollo client is not showing the extensions and exceptions.
Stringify the error and found that ,we can access it using error.graphQlErrors[0] , it's weird that it's not written anywhere in the documentation. This sure gave me a lot of trouble
The existing answer worked for me. Just providing some more details to help future Googlers.
On the apollo-server backend, we are throwing an error:
throw new UserInputError("Form Arguments invalid", {
field: "name",
});
On the apollo-client frontend, we are catching that error:
try {
// Perform GraphQL mutation.
} catch (error) {
// How do we get the "field" extra info?
}
As the previous answer says, there is some graphQlErrors property that contains extra information. So in this example, I can access error.graphQlErrors[0].extensions.field to get the "name" value.

Exception from Tracker recompute function ( Invariant Violation )

Weird behavior that I can't explain:
Here's a react component that shows a stringified object - data from google account.
If I change props.user[0].services to
props.user[0].services.google property
I get an error:
It might be that the services property is loaded after the function call.
The way I would solve this is using lodash to get default properties when there are none. It also prevents reference errors:
const googleProps = _.get(props, 'user[0].services.google', {
picture: 'https://some-source/default-picture.png',
email: 'unknown'
});

ReactTestUtils findRenderedComponentWithType throwing error not detecting by chai?

Im using findRenderedComponentWithType to make sure there is a error, and Im using chai's assert.throws, but it is not working.
first of all:
TestUtils.findRenderedComponentWithType documentation:
expects there to be one result and returns that one result, or throws exception if there is any other number of matches besides one.
When I use the function, Im getting an error (as expected and correct).
However I cant seems to assert it properly with chai:
I tried assert.throws(TestUtils.findRenderedComponentWithType(element, component), /(Error)/). But it is saying the test failed, even though I'm getting an error:
Error: Did not find exactly one match for componentType:function (props, context, updater) {
[...]
}
Check the signature of throws, it expects a function, not an error/object (which is the result of findRenderedComponentWithType.
http://chaijs.com/api/assert/#method_throws
So you will want to do something like
cons fn = () => TestUtils.findRenderedComponentWithType(element, component)
assert.throws(fn)

Resources