Swr: Possible "update on unmounted component" race condition

Created on 21 Jul 2020  路  2Comments  路  Source: vercel/swr

Bug report

It appears to be possible to trigger a React "Can't perform a React state update on an unmounted component" error based on a very specific set of circumstances involving a function component that queues a state update while re-rendering, ala "how do you implement getDerivedStateFromProps with hooks?".

The behavior was reported by @samselikoff, who narrowed it down to this repro project:

https://github.com/samselikoff/2020-07-16-show-new-tweets/tree/58757dd6c3486f2a149538275e7c2c92bceba19d

I then investigated the behavior he reported and was asking about.

Description / Observed Behavior

The home page component looks like this:

const fetcher = (url) => fetch(url).then((res) => res.json());

function Home() {
  const { data } = useSWR("/api/tweets", fetcher);
  let [buffer, setBuffer] = useState();

  if (data && !buffer) {
    setBuffer(data);
  }

  return <p>lorem ipsum</p>;
}

Home.headerTitle = "Latest Tweets";
Home.headerBorder = true;

export default Home;

When toggling back and forth between tabs, this component gets mounted and unmounted repeatedly. At some point, SWR tries to set state on a given instance of this component after it has been unmounted, causing that error to be thrown.

The error goes away if the setBuffer() call is put into either useEffect or useLayoutEffect. That said, a state setter here _is_ semantically legal ala gDSFP, and the error is _only_ thrown if that state setter is there.

Expected Behavior

SWR will never try to set state on an unmounted component,

Repro Steps / Code Example

https://github.com/samselikoff/2020-07-16-show-new-tweets/tree/58757dd6c3486f2a149538275e7c2c92bceba19d

  • Install and run app
  • Turn on React DevTools "Break on warnings" option
  • Repeatedly click back and forth rapidly between the "Home" tab and other tabs to force swapping of components

It'll probably take several tries, but it should eventually throw that "unmounted" error.

Additional Context

Chrome will show a very long async callstack, which includes use-swr.js:326 (built ESM version) where it's doing dispatch(newState); Checking the unmountedRef value at that point in time shows true:

SWR race condition (highlighted)

Inspecting the code, I see that SWR is calling requestIdleCallback. Something about the combination of that extra state setter causing an additional run of a render cycle, plus the timing of the idle callback, is causing SWR to try to set state after the instance unmounted.

My guess is that this particular revalidation function needs to check unmountedRef.current a bit earlier and bail out if necessary.

SWR version: 0.2.3

Most helpful comment

I verified that master has the fix + the problem went away in my code! Thank you 馃槃

All 2 comments

hi @marcelmokos , thanks for reporting! I belive it's the similar one to #494 , has been fixed in #510 .

I verified that master has the fix + the problem went away in my code! Thank you 馃槃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sergiodxa picture sergiodxa  路  4Comments

oran1248 picture oran1248  路  3Comments

baoduy picture baoduy  路  4Comments

tiagocorreiaalmeida picture tiagocorreiaalmeida  路  3Comments

nainardev picture nainardev  路  4Comments