Open hazae41 opened 3 years ago
could you try useSWR("/api/data", fetcher, { revalidateIfStale: false })
I tried and it doesn't revalidate, but I want it to revalidate if the stale data is older than the deduping interval
dedupingInterval is the time between two revalidate not the time between mutate and revalidate.
------------dedupingInterval----------
------mutate---revalidate----revalidate-- second one will be ignored
---mutate-----revalidate--- will still revalidate
if you want to send less requests, revalidateIfStale could save one request when hook mounted.
There should be a time between mutate a revalidate then
I have this same issue. Here's my use case:
I'm on React Native and am wrapping useSwr
in a custom hook, let's call it useSomeData({ paused: boolean })
. I want to be able to do a "soft" revalidation when various conditions are met: when I "unpause" useSomeData
, when some user state changes, etc. However, I want these revalidations to be deduped, and I don't want each instance of my useSomeData()
hook to make a separate request, as would be the case if I called mutate
in an effect.
fwiw I was able to solve this using undocumented dedupe
param in revalidate
argument in the pre-1.0.0 release: https://github.com/vercel/swr/blob/0.5.6/src/use-swr.ts#L360
so this does what I want it to do:
function useSomeData({ paused }) {
const readData = useSwr("some/key", myFetcher, { isPaused: () => paused })
const { revalidate, mutate } = readData
useEffect(() => {
// @ts-ignore dedupe is a revalidation option but not included on the type: https://github.com/vercel/swr/blob/0.5.6/src/use-swr.ts#L360
if (!paused) revalidate({ dedupe: true })
// note that calling 'mutate' here would make an api request for _each instance_ consuming this hook. I don't want that.
// mutate()
}, [paused, revalidate])
}
However, since revalidate
was removed in 1.0.0 I'm going to need to find a new path forward.
Each instance of my useSomeData() hook to make a separate request, as would be the case if I called mutate in an effect
This doesn't sound correct to me.
If multiple hooks mounted, calling mutate will only trigger one revalidate.
FYI: https://stackblitz.com/edit/nextjs-ybma55?file=pages%2Findex.js @computerjazz
BTW from your code snippet, I suppose the new hook in #1262 is what you need. @computerjazz
Thanks @promer94 and @shuding !
So, here's a more complete example of my use case: https://stackblitz.com/edit/nextjs-4xtgyg?file=pages%2Findex.js
Notice that if the fetch starts as paused: true
, and then toggles to paused: false
at some time later, a fetch does not occur by default, so we have to trigger it manually. Adding the useEffect
and calling mutate
results in each instance of the hook making its own separate fetch.
The new hook in #1262 looks like it may solve the issue as long as it deduplicates correctly, but I wonder if this specific case should be handled within the useSwr
hook itself in an effect that calls revalidate({ dedupe: true })
when config.isPaused()
state becomes false
?
And apologies if I'm steering this issue too far away from the original report. I'd be happy to open a new issue and move discussion there.
I also feel that it makes sense to dedupe all mutate
calls.
It feels like a pretty common pattern to ingest the same hook in multiple components, and allow SWR to handle the de-duping to avoid unnecessary re-renders. In React-Native, you have to handle revalidateOnFocus
on your own, something like the following:
const useProfile = () => {
const swr = useSwr('/users/me');
useFocusEffect(() => swr.mutate());
return swr;
}
const Profile = () => {
const { data } = useProfile();
return <ProfileHeader />
}
const ProfileHeader = () => {
const { data } = useProfile();
return <Foo />
}
Standard fetching is deduped correctly, and that's the biggest selling point of SWR for me. However, with this custom revalidation on focus for react-native, every call to mutate
, which would be every component that uses useProfile
, another request would be sent out.
In this example, two requests are being sent out, when I would expect them to be deduped. I don't really think it has anything to do with paused
property.
For those who don't want to scratch their heads anymore, wondering why the fuck swr does this, I made my own library.
https://github.com/hazae41/xswr
It uses composition-based hooks and has a very easy learning curve, and 0% weird behaviour.
I thought I was going mad, but turns out this is actually a thing. Why aren't mutate calls deduped too?!
I also have a similar problem, in the case of revalidateOnFocus=true
, when using useSWR("/list")
to provide list data for a page, if there is a refresh button on the page, click the button to use mutate( "/list")
Refresh list data, when switching to another window, and then click the refresh button to return to the page, re-focus and button click will initiate two requests for "/list"
I came here searching to make sure that mutate()
is independent and will always fulfill the desire to trigger a unique request to the server regardless of dedupingInterval
. I'm glad to see that's exactly how it works because if I (from my user's action) explicitly call mutate, I want it to do exactly that without exception.
Perhaps a middle ground is to have mutate()
or useSWR()
accept a configuration that allows for what the others are asking, so that they can make mutate()
dependent on / respect dedupingInterval
.
I am also trying to make mutate actually dedupe the requests. Is it possible?
I have a revalidateOnFocus middleware for react-native which currently causes 60 requests as its used in a list of 60 components. The default behavor works as useSWR only is called once as the key is the same, but trying to mutate the hooks based on focus will not dedupe.
I am also trying to make mutate actually dedupe the requests. Is it possible?
I have a revalidateOnFocus middleware for react-native which currently causes 60 requests as its used in a list of 60 components. The default behavor works as useSWR only is called once as the key is the same, but trying to mutate the hooks based on focus will not dedupe.
Honestly, pretty easy solution with swr
middleware
const dedupeCache = new Map();
export const revalidateOnFocus: Middleware = (useSWRNext) => {
return (key, fn, config) => {
const swr = useSWRNext(key, fn, config);
useFocusEffect(
useCallback(() => {
if (typeof swr.data === "undefined") return;
const serializedKey = unstable_serialize(key);
if (dedupeCache.has(serializedKey)) return;
dedupeCache.set(serializedKey, true);
swr.mutate().finally(() => {
dedupeCache.delete(serializedKey);
});
}, [swr.data, swr.mutate]),
);
return swr;
};
};
// Your query
const {data} = useSWR("/path", {
use: [revalidateOnFocus]
})
// Or global
<SWRConfig value={{ use: [revalidateOnFocus] }}>
{children}
</SWRConfig>
Bug report
Description / Observed Behavior
I prefetch and mutate some data when the user hovers a link using
mutate(key, data, false)
When the user clicks the link, the page is loaded and useSWR revalidates the data.However, the hover and the click where made within deduping interval.
Expected Behavior
Do not revalidate if
mutate(key, data, false)
has been called within the deduping intervalRepro Steps / Code Example
Additional Context
swr@1.0.0