-
-
Notifications
You must be signed in to change notification settings - Fork 689
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
Blocking resources: ResponseOptions
are lost / nested blocking resources don't block but stream.
#3280
Comments
Potentially cf #3048 |
Just so I am sure I understand what you're reporting correctly, now that I've finished my cup of coffee 😄 The idea here is that a new blocking resource that's created inside the children of What's clear to me: This data-loading pattern is not optimal and will lead to a slower response from the website and therefore worse user experience (and worse SEO, etc.) It introduces a waterfall, by not beginning to load the inner resource until after the outer Suspense is ready. Rather, resources should be hoisted up to the highest level possible. If the inner resource depends on the outer resource, it can still be hoisted up to that higher level, and i.e., in this case, simply hoisting the definition of I love a good MRE but sometimes they can make it hard to understand the broader context. Can you say a little more about the real use case that led to this pattern? What's not clear to me: I have a PR with a solution in #3282, which I've tested against the above example and it seems to fix it. I haven't tested it with more-deeply-nested sets of suspenses and blocking resources, and I haven't tested whether it has any ill effects elsewhere. |
#3282 seems to solve the repro and in general for the 2-layer case but not the general #[component]
pub fn MiddleComponent() -> impl IntoView {
let outer_r = OnceResource::new_blocking(async move {
#[cfg(feature = "ssr")]
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
Ok::<_, ()>(())
});
let inner_view = move || {
Suspend::new(async move {
let _ = outer_r.await;
view! { <InnerComponent /> }
})
};
view! {
<main>
<Suspense>{inner_view}</Suspense>
</main>
}
}
Correct!
Definitely agree this is mostly non-optimal and bad for user experience, but there's a lot of cases where you simply want portions of the app to block returning, until all the information to produce a meaningful UI is ready and you can pass it to the rest of the app, or have nodes of the app that need to add specific For the In my case my whole app is inside a blocking resource, to add some global state via But main thing I think is I'd definitely assume this would work, as the next user would, and was super hard to track down 😅 |
Fair enough. Blocking resources are definitely in the category of "things I wish I'd never implemented," for reasons exactly like this. Perhaps the answer all along should've been "if you need to do this, use This particular issue (resolving the first blocking resource immediately creates another blocking resource) is possible to solve, although apparently doing it in the n-ary case is relatively harder. But there will necessarily always be cases a user can create in which creating a blocking resource does not actually block, by burying it somewhere further down in the tree where it doesn't run until the response has already, irreversibly, begun. (Your "nodes of the app that need to add specific Any chance you'd be interested in exploring a way to solve the nested version here, that does better than my attempt in #3282? |
This would probably make sense as a user-warning. I would say anything blocking being streamed is a bug (this issue being leptos's bug but your case being userland misuse), seems like something's always wrong unless client-side or server "really blocking". I've been messing around with #3282 but not having much luck: I've been trying to drive the stream whilst continuously checking But not having much luck in getting the third blocking resource to run. So I think I'm off on what's going on here. |
Describe the bug
Nested blocking resources inside suspense don't end up blocking.
This becomes obvious when setting things to
ResponseOptions
in the nested resource and those values don't make it to the client, I believe because they end up streaming after the response is sent, instead of blocking correctly.I've used
OnceResource
's in the repro, but applies to normalResource::new_blocking
too.Leptos Dependencies
Rc2
To Reproduce
I've got a repro working from the
server_fns_axum
example, specific repro in codeblock below, but you can just checkout my fork and run that example: https://github.com/zakstucke/leptos/tree/suspense-bugcargo leptos watch
"Check cookie"
button, you'll see no cookie set (broken)<App />
body withview! { <InnerComponent /> }
, removing the outer suspense/blocking resource, retry and you'll see the cookie is now being correctly set.Broken with nested suspense/blocking resource:
Screen.Recording.2024-11-23.at.13.36.49.mov
Working after removing outer suspense/blocking resource:
Screen.Recording.2024-11-23.at.13.38.32.mov
The text was updated successfully, but these errors were encountered: