vercel / swr

React Hooks for Data Fetching
https://swr.vercel.app
MIT License
30.5k stars 1.22k forks source link

Re-rendering too often with suspense mode #454

Closed tbezman closed 3 years ago

tbezman commented 4 years ago

It seems like re-renders are happening too frequently when suspense mode is enabled.

Expected Behavior There should be one render to kick off the request, and then another render when the data is loaded.

Actual Behavior The component re-renders a total of 6 times. 4 times, the component throws to suspense. One more time after it has received the data. And then one more time for reasons I am unsure of.

Here is a minimum reproducible Sandbox.

sergiodxa commented 4 years ago

SWR will render one time without data, one with data as isValidating true and one with data and isValidating false. In development React will also render two times per every render, so you will get a total of 6 renders. Try it in production mode to check it renders 3 times.

tbezman commented 4 years ago

@sergiodxa Thanks so much for replying.

In regards to the isValidating state. Isn't it unnecessary to have that happen for the first render if we're suspending? Doesn't really matter either way I guess since it won't cause a repaint.

morgs32 commented 4 years ago

Ok about rerenders (and this may not have to do exactly with suspense)... Without suspense and without React.StrictMode I get 2 tidy rerenders: https://codesandbox.io/s/sharp-bell-xjhx4

But when you do a conditional fetch, once the key is valid you get 3 rerenders: https://codesandbox.io/s/condescending-nash-8v2lj?file=/src/App.js

mariopeixoto commented 4 years ago

Just wanted to put some thoughts on this matter out in the open...

I see the same behavior and I struggle to understand why useSWR triggers revalidation in suspense mode. Whenever using suspense mode the component renders 4 times:

1st render- throw to suspense while making request 2nd render- request is completed and data is returned (isValidating: false) 3rd render- revalidation is triggered and useSWR returns same data as 2nd render (isValidating: true) 4th render- revalidation is completed and again same data is returned (isValidating: false)

From these renders, I would think that useSWR made two requests (request on 1st render, and 2nd request on revalidation), but it actually makes only one (which is the correct behavior)... what I believe is wrong is the revalidation being triggered after the 2nd render.

The most intuitive behavior and what one would expect while using Suspense would be the following:

INITIAL RENDER: 1st render- throw to suspense while making request 2nd render- request is completed and data is returned (isValidating: false)

... then some time later if it needs to be revalidated

3rd render- throw to suspense while making request 4th render- request is completed and data is returned (isValidating: false)


I understand that it's supposed to return stale data while revalidating and having this behavior would throw SWR out of the picture, but when you're using the 'Suspense' feature, you expect to have it suspended when you have stale data (cache expired) or when you have no data in the cache.

Personally, whenever I use suspense mode, I ignore the isValidating... you have the latest data on the 2nd render anyway... whatever happens after that is not reflective of the requests that are happening behind the scenes... To be honest I'm only using suspense mode with useSWR because I don't want to add another fetching library like fetch-suspense to the projects that are already using useSWR...

mwest-cs commented 3 years ago

Just dropping to add a +1 to this issue. I'm seeing 4 renders exactly as @mariopeixoto describes. I've tried this both in dev and production modes and see the same behavior. I'd love to use suspense mode, but can't easily deal with the multiple renders issue.

koba04 commented 3 years ago

I've created a PR to fix this #979