SWR is an amazing react library that makes fetching data easier and more performant. What I really like about the library is the cache and deduplication. However, if we don't understand the mechanism correctly, it will lead you to a disaster. This post aims to take a look at some tricky cases of SWR caching and explain the reason.
The options onSuccess
Although we can configure the options onSuccess
with the same key in multiple hooks, only the one in the first mounted hook will be fired.
function Child ({index}) {
const { /* ... */ } = useSWR('/api/user', {onSuccess: () => console.log(`success ${index}`)});
return // ....
}
function App () {
return <>
<Child index={1}/>
<Child index={2}/>
<Child index={3}/>
<Child index={4}/>
</>
}
In the console:
success 0
There are four hooks with the key /api/user
, but due to the request deduplication in SWR, only the first hook will trigger the request. That's why only the onSuccess
in the first hooks is triggered.
So now we know that only the first hook of the same key will trigger the request, let's look at the following example
function Child ({index}) {
useSWR('/api/user', {onSuccess: () => console.log("Child success")});
return // ....
}
function Parent () {
useSWR('/api/user', {onSuccess: () => console.log("Parent success")});
return <Child/>
}
What will be the result in the console?
If your answer is "Child success", well done! The reason is simple. Because the hook inside the Child
component will be mounted first, so the request will be triggered by it but not the one in Parent
. So only the onSuccess
in Child
will be triggered.
The options dedupingInterval
Awesome! Now we have a clear understanding of how the request is being triggered and which onSuccess
will be fired. Let's look at the following example
function Child ({index}) {
const {data} = useSWR('/api/user', {onSuccess: () => console.log("Child success")});
return // ....
}
function Parent () {
const [showChild, setShowChild] = useState(false);
useSWR('/api/user', onSuccess: () => console.log("Parent success"));
useEffect(() => {
setTimeout(() => {
setShowChile(true);
}, 3000)
}, []);
return showChild && <Child/>
}
What will be the result in the console?
In this case, both "Parent success" and "Child success" will be shown. The reason is all about the option dedupingInterval
, which defaults to 2000ms. dedupingInterval
means during the interval, all the useSWR
with the same key will not fire request. So for the first and the second examples, the hooks are mounted within 2 seconds and this won't trigger another request. But for the third example, the second hooks are mounted after 3 seconds, which is greater than the dedupingInterval
.
Also, we should notice that the data
return from the hook in the Child
will be the cached data at first instead of undefined
. Please refer to this codesandbox to see the behavior.
Summary
Finally, we can understand the life cycle of SWR as follow:
- When the hook mounted, it will first check if there is data in the cache. If there is, set it to the
data
- Then check if there is any hook with the same key mounted before during the
depupingInterval
. If not, trigger the request. After the request success, theonSuccess
callback in the same hook will be fired.
Top comments (1)
Thanks!