Swr: Example for axios

Created on 21 Nov 2019  路  11Comments  路  Source: vercel/swr

With axios it's handy to "define requests" using a request object, for example like this:

import axios, { AxiosRequestConfig } from 'axios';

const request: AxiosRequestConfig = {
  baseURL: 'https://api.example.com',
  withCredentials: true,
  method: 'GET',
  url: '/search',
  params: {
    term: 'foobar',
  },
};

const response = await axios(request);
const data = response.data;

But from what I can gather, the key I can pass in to useSWR has to be a string? I can't find any of your included examples that uses anything other than a simple string either. In the readme, there's a short mention of using arrays, which can include both strings and objects, but it doesn't mention what the fetcher would actually look like, and it also says the key comparison is shallow, so with my request object above here, I assume that means a change in request.params.term would not be picked up?

Would it be possible for someone to add an example of how one can use useSWR with axios and a request object like this? Or is it perhaps not possible to do that?

documentation question

Most helpful comment

@Svish
Thank you for the example.
In case anyone interested, a typed version of this example:

import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"
import useSWR, { ConfigInterface } from "swr"

export const useSWRAxios = <Data extends {}>(
  request: Omit<AxiosRequestConfig, "baseURL" | "method" | "data">,
  config?: ConfigInterface,
) => {
  const requestModified: AxiosRequestConfig = {
    ...request,
    method: `GET`,
  }
  const configModified: ConfigInterface = {
    ...config,
    // global customizations
  }
  /**
   * Axios has a wrapper object around data => filter response
   */
  const { data: response, ...responseUseSWR } = useSWR<
    AxiosResponse<Data>,
    AxiosError
  >(
    JSON.stringify(requestModified),
    async () => axios(requestModified),
    configModified,
  )
  const { data, ...responseAxios } = response || {}
  return { ...responseUseSWR, responseAxios, data }
}

All 11 comments

When using an array as the key, SWR will pass those items as arguments to the fetcher function:

useSWR([A, B, C, ...], fetcher)
// calls fetcher(A, B, C, ...)

For your specific case, I'd do something like:

// global config because this part won't change
const getRequest: AxiosRequestConfig = {
  baseURL: 'https://api.example.com',
  withCredentials: true,
  method: 'GET'
};

function App () {
  // inside component
  const params = useMemo(() => ({ term }), [term])
  useSWR(
    ['/search', params],
    (url, params) => axios({ ...getRequest, url, params }).then(res => res.data)
  )
}

or simpler:

// global config because this part won't change
const getRequest: AxiosRequestConfig = {
  baseURL: 'https://api.example.com',
  withCredentials: true,
  method: 'GET'
};

const fetchTerm = (url, term) =>
  axios({ ...getRequest, url, params: { term } }).then(res => res.data)

function App () {
  // inside component
  useSWR(['/search', term], fetchTerm)
}

The major reason of shallow comparison is performance. So it will always render faster, and it will reuse the stale data if the same url + params pair has occurred before.

Thanks for the feedback! We will definitely improve the readme and create an example for axios.

@quietshu Thank you for a quick reply!

In your example you have pulled the params out of the request object, which I was hoping to avoid as it's super useful to have it all contained in one request object. I definitely get the need for performance, but for small objects like these, would a deep equality check really be much slower? Would it maybe be possible to add the option of switching to a deep equality check?

@Svish I see your point 馃憤 I think this might be helpful then:

// let url = '/search'
// let term = 'foobar'

useSWR([url, term], () => axios({
  baseURL: 'https://api.example.com',
  withCredentials: true,
  method: 'GET',
  url,
  params: { term }
}).then(res => res.data))

key is just an indicator of this specific request, you can totally ignore it from the fetcher params. Just make sure the request will always be associated with the key (if key changes, the request will change).

So, would something along the following work? 馃

// useRequest.js
export default function useRequest(request, config) {
  const { data: response, error, isValidating, revalidate } = useSWR(
    JSON.stringify(request),
    () => axios(request),
    config
  );

  return {
    data: response && response.data,
    response,
    error,
    isValidating,
    revalidate,
  };
}
const { data } = useRequest({
  baseURL: 'https://api.example.com',
  withCredentials: true,
  method: 'GET',
  url: '/search',
  params: {
    term: 'foobar',
  },
});

It will work :)

@quietshu Quick question... does config have any effect on whether useSWR decides to revalidate? I.e. do I need to useMemo if the config might not be the same object every time (even though it has the same contents), or would that be unnecessary?

@Svish nope, config change doesn't trigger revalidation (so you don't need to wrap it with useMemo, you can just change it). The only exception is config.refreshInterval. If that changes, SWR will adjust the polling interval.

@Svish
Thank you for the example.
In case anyone interested, a typed version of this example:

import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"
import useSWR, { ConfigInterface } from "swr"

export const useSWRAxios = <Data extends {}>(
  request: Omit<AxiosRequestConfig, "baseURL" | "method" | "data">,
  config?: ConfigInterface,
) => {
  const requestModified: AxiosRequestConfig = {
    ...request,
    method: `GET`,
  }
  const configModified: ConfigInterface = {
    ...config,
    // global customizations
  }
  /**
   * Axios has a wrapper object around data => filter response
   */
  const { data: response, ...responseUseSWR } = useSWR<
    AxiosResponse<Data>,
    AxiosError
  >(
    JSON.stringify(requestModified),
    async () => axios(requestModified),
    configModified,
  )
  const { data, ...responseAxios } = response || {}
  return { ...responseUseSWR, responseAxios, data }
}

@o-alexandrov If you check out the pr i added for this, I did add one for typescript 馃槈

https://github.com/zeit/swr/blob/master/examples/axios-typescript/libs/useRequest.ts

As a noob end-user the useRequest axios example just works, awesome. I do have a feeling that some swr features like trigger no longer "just work". If confirmed, I could add a small disclaimer to the examples.

How do you folks handle updating the cache with this useRequest helper? Seems like an anti-pattern whenever there is already a good pattern that accepts any fetch implementation.

Sorry, not trying to dump on the helper, I'm just worried this Issue will cause people to reach for the useRequest implementation when they inevitably Google for _"swr axios"_ (like I did) and completely overlook one of SWR's best features: cache.

I'm new to SWR, so maybe I'm missing something. If so, I'd appreciate anyone educating me. 馃檱

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DoT214 picture DoT214  路  4Comments

sergiodxa picture sergiodxa  路  4Comments

frdwhite24 picture frdwhite24  路  4Comments

bcomnes picture bcomnes  路  3Comments

baoduy picture baoduy  路  4Comments