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, theonSuccesscallback in the same hook will be fired.
Top comments (1)
Thanks!