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

onBecomeUnobserved not triggered immediately on useObserver component unmount #3774

Open
Wroud opened this issue Oct 17, 2023 · 10 comments
Open
Labels

Comments

@Wroud
Copy link

Wroud commented Oct 17, 2023

Intended outcome:
onBecomeUnobserved triggered when the component wrapped with observer is unmounted

Actual outcome:
onBecomeUnobserved never triggered or triggered randomly

How to reproduce the issue:

https://codesandbox.io/s/practical-ioana-6sthfs?file=/src/App.tsx

Versions

seems like it's reproduced on any version

@Wroud Wroud added the 🐛 bug label Oct 17, 2023
@Wroud
Copy link
Author

Wroud commented Oct 17, 2023

It only reproduces with React.StrictMode

@Wroud
Copy link
Author

Wroud commented Oct 17, 2023

same issue with react suspense: https://codesandbox.io/s/goofy-wright-kmpjxd?file=/src/App.tsx

@Wroud
Copy link
Author

Wroud commented Oct 18, 2023

Ideas:

  1. on render, use reaction to collect used observables and interrupt render if some changes
  2. destroy the first reaction after executing the render function but save a list of used observables
  3. use useEffect to re-create reaction after the component is mounted with the saved list of used observables

This approach tries to reach two goals:

  1. save react behavior with render interruption on state change when rendering component
  2. clears reaction and its dependencies to support React.StrictMode and React Suspense (you don't need to track 'lost' reactions in this case also)

Why to use useEffect to recreate reaction:

  1. server rendering support (useEffect won't be called in SSR)
  2. executed after component mounted (Suspense)

@urugator
Copy link
Collaborator

It takes a while, but it eventually unobserves (at least for me):
image

@Wroud
Copy link
Author

Wroud commented Oct 18, 2023

Does it work for both examples?

@Wroud
Copy link
Author

Wroud commented Oct 18, 2023

Yes, it's released, but the delay is long; for example, if I want to track used resources to load them asynchronously, it will cause loading data even when a component is already unmounted.

@urugator
Copy link
Collaborator

Yes, the suspense example actually unobserved immediately upon clicking the btn on the first try. It depends on GC, so it's a bit random, which I agree isn't ideal, but I wouldn't expect changes here anytime soon. Ideally use useEffect if precise timing is needed. Would be good to mention this in docs though (PR welcome).

use useEffect to re-create reaction after the component is mounted with the saved list of used observables

You also have to diff saved and current values as your state could be changed before mount. We were considering these before. I am a bit afraid that the machinery involved could often end up being slower or comparable to just scheduling another render on mount...

@Wroud
Copy link
Author

Wroud commented Oct 19, 2023

I see. Why are you using setTimeout with a custom timeout to clear lost reactions?
Is it beneficial to use requestAnimationFrame and cancelAnimationFrame instead?

Theoretically, when the component hasn't been rendered on the following browser's repaint, it's optimized by React and it will be rendered in the following frames, or the render has been interrupted by StrictMode or Suspense.

Wroud added a commit to Wroud/mobx that referenced this issue Oct 19, 2023
…de and Suspense

onBecomeUnobserved never triggered with observer component mobxjs#3774
@Wroud
Copy link
Author

Wroud commented Oct 19, 2023

I created a pull request that includes the use of requestAnimationFrame and a new additional test case.
I also tested it on my project where I found this issue initially

@Wroud Wroud changed the title onBecomeUnobserved never triggered with observer component onBecomeUnobserved not triggered immediately on useObserver component unmount Oct 19, 2023
@Wroud Wroud changed the title onBecomeUnobserved not triggered immediately on useObserver component unmount onBecomeUnobserved not triggered immediately on useObserver component unmount Oct 21, 2023
@joshuakb2
Copy link

I just encountered this bug too. I made a reproduction, not sure if it helps but I'll link it just in case.
https://codesandbox.io/s/mobx-leak-repro-8wph2n?file=/src/App.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants