Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use-subscription causes UI tearing in some random cases #16396

milankinen opened this issue Aug 14, 2019 · 4 comments · Fixed by #16623


Copy link

commented Aug 14, 2019

Do you want to request a feature or report a bug?


What is the current behavior?

Referring to this twitter conversation, it seems that use-subscription can cause "UI tearing" in some random cases due to a (possible) race condition w.r.t. the combination of subscribe and getCurrentValue in internal usage.

Here is a minimalistic application demonstrating the behaviour:

What is the expected behavior?

I'd expect counters in the example application to always render same number.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

Tested React version: 16.9.0 (haven't tested with older ones)
OS: OSX 10.14.6
Hardware: MacBook Pro (Retina, 15-inch, Mid 2015) 2.8 GHz Intel Core i7 & 16GB RAM
Browser: Chrome Version 76.0.3809.100 (Official Build) (64-bit)



This comment has been minimized.

Copy link

commented Aug 15, 2019


This comment has been minimized.

Copy link

commented Aug 30, 2019

This is an interesting case.

For me, the first mutation pair (that doesn't tear) proceeds like so:

  1. Mutation happens
  2. All useSubscription change callbacks fire and schedule work with React
  3. React eagerly runs the state updater functions for all components
  4. Async rendering starts, yielding every once and a while because components are intentionally expensive
  5. Second mutation fires while rendering is still in progress (between frames).
  6. Updates are scheduled with React, but updater functions aren't run until after the rendering finishes.

For the second mutation pair (the one that tears) the sequence is different in an important way:

  1. Mutation happens
  2. All useSubscription change callbacks fire and schedule work with React
  3. State updater functions don't run
  4. Async rendering starts
  5. Individual state updater functions are run before each component is rendered
  6. The second mutation fires between these renders
  7. Subsequent state updater functions that run get the newer value.

I could "fix" the tearing in this case by reading the value in the outer change handler (outside of the state updater function, rather than inside of it) and I probably should make this change anyway. I'd like to better understand why this is happening first though.

I'm not sure yet why the different behavior occurs. It looks like React doesn't eagerly compute the state in the second case because expirationTime !== NoWork. I'm not sure if this is expected though, given that (at least in my test) I've flushed all pending work.

The good news is that I can catch this in a test. I had trouble at first, until I realized that the first interruption didn't cause the tear - but the second one did. (Test also requires two.)


This comment has been minimized.

Copy link

commented Oct 22, 2019

cc @milankinen @dai-shi Have you retried the latest version of use-subscription (with the fix from #16623) to see if you still see tearing?


This comment has been minimized.

Copy link

commented Oct 22, 2019

I tried with v1.1.0 and confirmed that tearing is fixed.

Still check4 is failing. It might be rare edge cases.
It's a case a parent component renders during heavy render in children. Somehow it stops updating. Users can see it. Appreciated if you could take time to review it. (I tried to organize my test code since you previously mentioned some difficulties in reading it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
4 participants
You can’t perform that action at this time.