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

useEffect didn't run in some rare case (lazy + memo) #4631

Open
1 task done
guoyunhe opened this issue Jan 13, 2025 · 7 comments
Open
1 task done

useEffect didn't run in some rare case (lazy + memo) #4631

guoyunhe opened this issue Jan 13, 2025 · 7 comments

Comments

@guoyunhe
Copy link

  • Check if updating to the latest Preact version resolves the issue

Describe the bug

I have a simple app with the following structure:

  • App (suspense)
    • Foobar (lazy)
      • Foo (memo)
      • Bar (lazy)

I found that in this case, Foo's useEffect() will NOT be executed. (But it does in React 18)

To Reproduce

https://codesandbox.io/p/sandbox/preact-memo-gpmrxk

Expected behavior

Show foobar

Actual behavior

Show bar

@JoviDeCroock
Copy link
Member

JoviDeCroock commented Jan 13, 2025

This shortcoming originates from the detaching logic, located here currently it bubbles up in a straight line but it should reset every child up until the Suspense boundary, including siblings.

Edit: wait it does, but somehow the memo'd component just bails... we might need to set _force to true - I guess a similar fix would be needed for #3449 either way

@CaptainWang98
Copy link

I noticed the bug disappears if replace the memo(Foo) with Foo;
I spend several days but get no idea, can I get some clue here?

@JoviDeCroock
Copy link
Member

As outlined in my comment it's a bug with memoization and can be addressed in the detaching logic by i.e. setting _force to true.

@CaptainWang98
Copy link

I may have some misunderstanding about the issue.
So I reviewed the reproduce. Here is my conclusion about why this bug happen:

The Lazy(Bar) get the wrong Suspense boundary, causing bailed the foo and bar;

We can resolve it either:

  1. set _force in detachedClone to make sure every descendants update;
  2. or set correct Suspense boundary for every Lazy Comp;

And we choose to use plan 1. Do I get your thought correctly?
🤔 But I'm also wondering if plan 2 is better?

@JoviDeCroock
Copy link
Member

JoviDeCroock commented Mar 2, 2025

That's not right, everything works correctly it's just that the effect isn't reexecuted which your PR's test is not testing.

As you can see in the reproduction a few things happen, one of which is that the sibling lazy boundary suspends. This means that detachedClone will cancel any effects that are bound to run after commit. The memoized component containing the effect does render successfully which means that by definition it will become memoized and unless props change it won't rerender.

What we want instead is for this component to be successfully remounted which we can trigger by means of _force.

Your test is almost right in your branch with the exception that you are not testing that the effect inside of the memoized boundary is re-executed when the lazy boundary resolves.

@CaptainWang98
Copy link

Got what you mean. But here, if they don't wrapped by <div>, the bug disappears.
As detachedClone will recursively apply to all Suspense's descendant components, I don't know why the bug disappears only by remove <div>.

@CaptainWang98
Copy link

I think I get the answer. Cuz without div, the Memoed Comp has the same _parentDom with detachedParent, then get set _force.

CaptainWang98 pushed a commit to CaptainWang98/preact that referenced this issue Mar 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants