DEV Community

loading...

How to understand the request deduplication in SWR

andykao1213 profile image Andrew Kao ・3 min read

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}/>
  </>
}
Enter fullscreen mode Exit fullscreen mode

In the console:

success 0
Enter fullscreen mode Exit fullscreen mode

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/>
}
Enter fullscreen mode Exit fullscreen mode

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/>
}
Enter fullscreen mode Exit fullscreen mode

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, the onSuccess callback in the same hook will be fired.

Discussion (0)

pic
Editor guide