How to understand the request deduplication in SWR
Tags: react, swr, javascript
Published: 2021-04-17 07:52:53

[SWR](https://swr.vercel.app/) 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](https://swr.vercel.app/advanced/performance#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](https://swr.vercel.app/docs/options#options) `onSuccess` with the same key in multiple hooks, only the one in the first mounted hook will be fired. 

```jsx
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

```jsx
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

```jsx
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](https://codesandbox.io/s/swr-depuping-lexoz?file=/src/Deduping.jsx) 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.