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

Problems to make it work with TypeScript and axios #31

Closed
svschannak opened this issue Mar 13, 2019 · 15 comments
Closed

Problems to make it work with TypeScript and axios #31

svschannak opened this issue Mar 13, 2019 · 15 comments

Comments

@svschannak
Copy link

@svschannak svschannak commented Mar 13, 2019

I am trying to use react-async in an app with TypeScript and Axios, but can't find any documentation about it, would be great to find som, because they are pretty popular. If you can help me with my problem i would be happy to create a PR or write an article about it, because i really like the idea of your package.

My basic problem is that i have a function that handles the fetch via axios with async/await:

    const fetchCategories = async() => {
        const response = await axios.get("/route/");
        return response;
    }

And in my Component i use the following:

<Async promiseFn={fetchCategories}>...</Async>

Now, TS throws the following error:

JSX element type 'Async<AxiosResponse<any>>' is not a constructor function for JSX elements.
  Type 'Async<AxiosResponse<any>>' is missing the following properties from type 'Element': type, key

But i don't really understand what is going on here - what kind of response does promiseFN expect, because 'Element': type, key seems to be very generic. Or did i understand something fundamentally wrong?

@svschannak
Copy link
Author

@svschannak svschannak commented Mar 13, 2019

I actually got it working, when i started to use useAsync.

But it is failing, when I want to pass custom props like this:

const fetchCatalogData = async (props:any) => {
    const response = await api.get(`${apiRoutes.catalog}/${props.catalogId}/`);
    return response.data;
}
const { data, error, isLoading } = useAsync({ promiseFn: fetchCatalogData, catalogId: 1 });

The reason for this is that the type definitions won't accept any custom parameters. I mean it can, if you declare your interface as any. But this would work against the advantages of TypeScript. What I actually did was changing the interface of AsyncOptions to the following code:

interface AsyncOptions<T> {
  promise?: Promise<T>
  promiseFn?: (props: object, controller: AbortController) => Promise<T>
  deferFn?: (args: any[], props: object, controller: AbortController) => Promise<T>
  watch?: any
  watchFn?: (props: object, prevProps: object) => any
  initialValue?: T
  onResolve?: (data: T) => void
  onReject?: (error: Error) => void
  parameters?: object
}

As you can see, i added the parameters type to pass extra parameters to the Promise function and it actually works. Is there any other way to do this with TypeScript or should I create an PR for this?

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Mar 14, 2019

Both <Async> and useAsync() should be able to work with Axios and async/await. TypeScript seems to be complaining about the properties that <Async> exposes. Could you maybe create a CodeSandbox that reproduces the error?

It's not really a surprise that useAsync "fixes" the error, because it doesn't create an Element at all. As for the type signature for additional props, it seems you're suggesting the addition of a parameters prop/option, correct? That would mean an API change that offers an alternative way to pass additional props. I prefer to keep the API the way it is, and instead update the TS typings to be compatible with the existing API. I think we can achieve this using an indexer:

interface AsyncOptions<T> {
  promise?: Promise<T>
  promiseFn?: (props: object, controller: AbortController) => Promise<T>
  deferFn?: (args: any[], props: object, controller: AbortController) => Promise<T>
  watch?: any
  watchFn?: (props: object, prevProps: object) => any
  initialValue?: T
  onResolve?: (data: T) => void
  onReject?: (error: Error) => void
  [prop: string]: any
}

Note the last statement, which specifies an indexer. Will that work for you?

@svschannak
Copy link
Author

@svschannak svschannak commented Mar 14, 2019

Yes, that works pretty well and it won't affect any current code.

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Mar 14, 2019

Cool. Published in v5.1.1.

@ghengeveld ghengeveld closed this Mar 14, 2019
@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Mar 14, 2019

Thanks for reporting!

@svschannak
Copy link
Author

@svschannak svschannak commented Mar 15, 2019

I just leave my blog entry about this here if somebody else wants to get it work with TypeScript: https://www.schannak.com/posts/react-async-typescript/

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Mar 16, 2019

Awesome! Care to link to https://react-async.dev from the article? I'm trying to get React Async higher up in Google 😇

@svschannak
Copy link
Author

@svschannak svschannak commented Mar 18, 2019

Done ;).

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Mar 27, 2019

Hey @svschannak I just created a pre-release for #37 which greatly improves usability with TypeScript. You can install it as react-async@6.0.0-0 or react-async@next. It will soon be released, but I'd like to gather some feedback first. Care to take a look and try it out?

@svschannak
Copy link
Author

@svschannak svschannak commented Apr 1, 2019

@ghengeveld I will try it out. But I think you have already released it, right?

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Apr 1, 2019

Yes, it's released now. You can just use react-async@latest.

@alexispcode
Copy link

@alexispcode alexispcode commented May 24, 2019

Hi, I'm having some problems trying to make cancelation works on axios in typescript. Is it possible?

const loadData = async ({ todoId }: any, signal: any) => {
  return await axios
    .get('https://jsonplaceholder.typicode.com/todos/${todoId}', {
      cancelToken: signal
    })
    .then(({ data }) => data);
};

const { data, isLoading, reload, cancel } = useAsync({
    promiseFn: loadData,
    todoId: 1
  });
@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Jun 3, 2019

Axios is not compatible with the AbortController API which React Async uses, so you need to setup cancellation manually. Something like this should do it:

const source = axios.CancelToken.source();

const loadData = async ({ todoId }: any) => {
  return await axios
    .get('https://jsonplaceholder.typicode.com/todos/${todoId}', {
      cancelToken: source.token
    })
    .then(({ data }) => data);
};

const { data, isLoading, reload, cancel } = useAsync({
  promiseFn: loadData,
  onReject: e => e.name === "AbortError" && source.cancel(e.message),
  todoId: 1
});

I opened #53 to document this. Let me know if it works for you.

@cargallo
Copy link

@cargallo cargallo commented Nov 21, 2019

Hi, How can I avoid setting the parameter definition of the async function like... { todoId }: any and specify it with the business type?

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Nov 28, 2019

There is proper built-in support for these argument types unfortunately, it's a limitation of the current API. We want to fix this in the future but I need some help to make that happen. I'm not that familiar with TypeScript yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants