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

useId in Suspense boundary re-render resets counter #407

Closed
f0x52 opened this issue Jan 8, 2025 · 1 comment · Fixed by #411
Closed

useId in Suspense boundary re-render resets counter #407

f0x52 opened this issue Jan 8, 2025 · 1 comment · Fixed by #411

Comments

@f0x52
Copy link
Contributor

f0x52 commented Jan 8, 2025

I'm using a combination of useId and useContext hooks to match components across separate passes in a streaming re-render.

<AsyncContext.Provider>
  <Suspense fallback="suspense-1">
    <ComponentOne/>
  </Suspense>
  <Suspense fallback="suspense-1">
    <ComponentTwo/>
  </Suspense>
</AsyncContext.Provider>

On the first render pass, ComponentOne gets id P0-0 and ComponentTwo P0-1, as expected. When the second Suspense branch gets re-rendered after resolving the thrown Promise, ComponentTwo useId is P0-0 instead.
From my cursory understanding of preactjs/preact#3773 I suppose this is caused by the walking algorithm only walking up-to the second Suspense boundary for the re-render, thus ending up with P0-0 instead of the P0-1 of the first pass.
Which might actually align with

This behavior is similar to before where ids have the constraint that they are unique per root, not globally unique.
Where the first render, and the suspended render could be seen as having different roots? It would still be nice to have a workaround or fix for this :)

Codesandbox: https://codesandbox.io/p/devbox/gifted-turing-2khmts?workspaceId=ws_Hyb2mUcrQ6TVf2LBce8QH7

Similar to #406, but useId doesn't get stuck here, though it causes a different issue. I'm also not sure why this doesn't run into the same infinite loop.

Debug output:

<initial render pass>
render ComponentOne
  running/throwing promise for P0-0 
>> suspense-1 suspends here
render ComponentTwo
  running/throwing promise for P0-1
>> suspense-2 suspends here
  resolved promise for P0-0
>> suspense-1 resolves, branch re-renders
render ComponentOne
  returning result for P0-0
>> suspense-1 finishes final render
  resolved promise for P0-1
>> suspense-2 resolves, branch re-renders
render ComponentTwo
  returning result for P0-0 <-- useId hook unexpectedly gets P0-0 instead of P0-1, causing it to re-use the P0-0 result instead of that from it's own Promise
@f0x52
Copy link
Contributor Author

f0x52 commented Jan 8, 2025

Reproducible with renderToStringAsync

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

Successfully merging a pull request may close this issue.

1 participant