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

Fix: useDeferredValue initialValue suspends forever without switching to final #27888

Merged
merged 1 commit into from
Jan 8, 2024

Commits on Jan 7, 2024

  1. Fix: uDV initialValue suspends without switching to final

    Fixes a bug in the experimental `initialValue` option for
    `useDeferredValue` (added in facebook#27500).
    
    If rendering the `initialValue` causes the tree to suspend, React should
    skip it and switch to rendering the final value instead. It should not
    wait for `initialValue` to resolve.
    
    This is not just an optimization, because in some cases the initial
    value may _never_ resolve — intentionally. For example, if the
    application does not provide an instant fallback state. This capability
    is, in fact, the primary motivation for the `initialValue` API.
    
    I mostly implemented this correctly in the original PR, but I missed
    some cases where it wasn't working:
    
    - If there's no Suspense boundary between the `useDeferredValue` hook
    and the component that suspends, and we're not in the shell of the
    transition (i.e. there's a parent Suspense boundary wrapping the
    `useDeferredValue` hook), the deferred task would get
    incorrectly dropped.
    - Similarly, if there's no Suspense boundary between the
    `useDeferredValue` hook and the component that suspends, and we're
    rendering a synchronous update, the deferred task would get incorrectly
    dropped.
    
    What these cases have in common is that it causes the `useDeferredValue`
    hook itself to be replaced by a Suspense fallback. The fix was the same
    for both. (It already worked in cases where there's no Suspense
    fallback at all, because those are handled differently, at the root.)
    
    The way I discovered this was when investigating a particular bug in
    Next.js that would happen during a 'popstate' transition (back/forward),
    but not during a regular navigation. That's because we render popstate
    transitions synchronously to preserve browser's scroll position — which
    in this case triggered the second scenario above.
    acdlite committed Jan 7, 2024
    Configuration menu
    Copy the full SHA
    61cde78 View commit details
    Browse the repository at this point in the history