Swr: how to use post method and pass params in swr?

Created on 7 Nov 2019  路  18Comments  路  Source: vercel/swr

Most helpful comment

function App() {
  const { data } = useSWR('/api/data', fetch) // <--------------GET

  return <>
    {data}
    <button onClick={() => {
      fetch('/api/data', { method: 'POST', body: ... })  // <---POST
    }}>update data</button>
  </>
}

You can just use fetch for POST requests. I don't see the benefit of allowing POST in SWR.
And most importantly, most of the great features will be gone with POST (because it's not a "pure" action like GET, which is perfect for fetch-as-you-render):

  • stale-while-revalidate (SWR)
  • focus revalidate / polling
  • automatically request deduplication
  • caching
  • ...

But speaking of revalidation/mutation, it's still a thing for allowing POST together with SWR. Currently we need to do this:

mutate('/api/data', newData, false) // local mutate without revalidation
await sendPost('/api/data', newData) // POST request
trigger('/api/data') // revalidate

Maybe we can simplify it to something like:

mutate('/api/data', newData, sendPost)

which will do the exact same thing as above.

All 18 comments

const fetcher = params => url => post(url, params)
const { data, error } = useSWR('/api/user', fetcher({key: val}))

This way?

SWR is more intended to get data, not update it, usually you have another function to update and after it you will run mutate to update the cache and trigger a revalidation (SWR will fetch it again)

This example https://github.com/zeit/swr/tree/master/examples/optimistic-ui does something similar, update and then revalidate

function App() {
  const { data } = useSWR('/api/data', fetch) // <--------------GET

  return <>
    {data}
    <button onClick={() => {
      fetch('/api/data', { method: 'POST', body: ... })  // <---POST
    }}>update data</button>
  </>
}

You can just use fetch for POST requests. I don't see the benefit of allowing POST in SWR.
And most importantly, most of the great features will be gone with POST (because it's not a "pure" action like GET, which is perfect for fetch-as-you-render):

  • stale-while-revalidate (SWR)
  • focus revalidate / polling
  • automatically request deduplication
  • caching
  • ...

But speaking of revalidation/mutation, it's still a thing for allowing POST together with SWR. Currently we need to do this:

mutate('/api/data', newData, false) // local mutate without revalidation
await sendPost('/api/data', newData) // POST request
trigger('/api/data') // revalidate

Maybe we can simplify it to something like:

mutate('/api/data', newData, sendPost)

which will do the exact same thing as above.

Maybe it'd be worth checking out react-query's useMutation() hook for reference as well.

I have a use case with firebase onCall function(which it's method is POST). Because it supports authentication by default, I have to use this workaround.

import firebase from '../lib/firebase'

export const fetchSubmissionData = async (id: string) => {
  const getSubmission = firebase
    .app()
    .functions('asia-east2')
    .httpsCallable('getDetailedSubmissionData')

  return await getSubmission({ submission_id: id })
}

Then I can use

 const { data } = useSWR(`${id}`, fetchSubmissionData)

My function is onCall because it needs to verify user before sending back data.

Any idea on improving this? @quietshu

@iammarkps can you show me a POST example you have without using SWR?

I always use it that way. But I'm just curious that, can I have only 1 global function to use firebase callable function?

Now I have to implement 3 different functions with same logic.

https://github.com/programming-in-th/programming.in.th/blob/master/src/utils/fetcher.ts

Because firebase callable function receives 2 args, not 1 like normal GET request(Just url of API).
Can SWR pass 2 arguments to functions?

PS.Pardon my bad english

I've used to do this when I was using redux (It's POST method).

https://github.com/programming-in-th/programming.in.th/blob/83ff9a23648e9ece55f8abeb555292da28908dac/src/redux/actions/submission.ts#L24

and this for normal data fetching (Still POST method)

https://github.com/programming-in-th/programming.in.th/blob/83ff9a23648e9ece55f8abeb555292da28908dac/src/redux/actions/task.ts#L34

But now I've converted it to normal http GET not firebase callable anymore.

https://github.com/programming-in-th/cloud_functions/blob/master/functions/src/tasks.ts#L4

So it works perfectly with SWR.

But I can't convert this to http GET, because I have to check user right to access the code in line 187. So I need to depend on firebase request context. @quietshu

I want to do this

export const fetchFromFirebase = async (type: string, param?: Object) => {
    const get = firebase
    .app()
    .functions('asia-east2')
    .httpsCallable(type)

  return await get(param)
}

@iammarkps thanks for elaborating! 馃憤

Now I can understand your use case, it makes perfect sense.

Can SWR pass 2 arguments to functions?

And yes! This feature will solve the problem: https://github.com/zeit/swr/pull/98

We just shipped it to [email protected] and you can use it but the documentation is not yet updated. Ideally your code will be:

useSWR([type, param], fetchFromFirebase)

But keep in mind it compares arguments shallowly, so keep in mind to memorize the object:

const param = useMemo(() => ({ submission_id: id }), [id])
useSWR(['getDetailedSubmissionData', param], fetchFromFirebase)

@quietshu Thank you! I've just improved it with your suggestion in this commit

@iammarkps looks great!

So, this should be work in the newest version (dependent data and multiple params):

const { data: user } = useSWR('/api/user')
const params = useMemo(() => {
  return { username: user && user.username }
}, [user])

const profile = useSWR(() => ['/api/profile', params])

Because I'm trying this and I'm getting an infinite loop always 馃槙

@pedronauck oh that's because you're using a function which returns an array here:

const profile = useSWR(() => ['/api/profile', params])

This case is not yet covered (thanks for reporting!), for now you can just use an array:

const profile = useSWR(['/api/profile', params])

I Used like this and it worked for me, please let me know If I鈥檓 missing something

let url = 'some url here';

export default function ComponentName  (props) {
  const fetcher = (...args) => fetch(url, {
    method: 'post',
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(props.payload)
  }).then(res => res.json())

  const { data, error} = useSWR(url, fetcher, { suspense: true });

Hi! Great library, I had a quick question:

const profile = useSWR(() => ['/api/profile', params])

Right now, it's fetching whenever any attribute of params changes. Is there a way for me to programatically make it fetch with updated params only when a specific attribute of params changes?

Thanks

You need to pass every attribute inside params as different elements of the array.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bcomnes picture bcomnes  路  3Comments

bywo picture bywo  路  4Comments

loveholly picture loveholly  路  3Comments

baoduy picture baoduy  路  4Comments

sergiodxa picture sergiodxa  路  4Comments