GithubHelp home page GithubHelp logo

Comments (13)

ealmloff avatar ealmloff commented on May 18, 2024 1

use_server_future suspends the component here:

let recipe = use_server_future(move || {
    get_recipe(id.clone())
})?;

The ? will return None (and render a placeholder) when the future is resolving. We don't do a full reload of the page because it generally won't make the future resolve any faster and it would reset the state in your page.

Suspense boundaries are not implemented yet, but once they are you will be able to handle rendering a placeholder higher up in your component.

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

To be more exact the version I'm using is

dioxus = { git = "https://github.com/ealmloff/dioxus.git", branch = "fix-fullstack-history"

since routing was broken in 0.5.0 alpha.

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

Also would be great to have more debug logging in Dioxus, to better see what's going on under the hood when something like this happens.

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

It's always the last rsx expression that gets sent over the websocket when anything changes. So changing the order of match clauses will change the behavior.

from dioxus.

ealmloff avatar ealmloff commented on May 18, 2024

If you set DIOXUS_LOG="trace", the CLI will log some information about what it thought changed between the new and old versions of the code.

Some part of the code diffing rsx here may be broken:

(syn::Expr::Match(new_expr), syn::Expr::Match(old_expr)) => {
if find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls) {
return true;
}
for (new_arm, old_arm) in new_expr.arms.iter().zip(old_expr.arms.iter()) {
match (&new_arm.guard, &old_arm.guard) {
(Some((new_tok, new_expr)), Some((old_tok, old_expr))) => {
if find_rsx_expr(new_expr, old_expr, rsx_calls) || new_tok != old_tok {
return true;
}
}
(None, None) => (),
_ => return true,
}
if find_rsx_expr(&new_arm.body, &old_arm.body, rsx_calls)
|| new_arm.attrs != old_arm.attrs
|| new_arm.pat != old_arm.pat
|| new_arm.fat_arrow_token != old_arm.fat_arrow_token
|| new_arm.comma != old_arm.comma
{
return true;
}
}
new_expr.attrs != old_expr.attrs
|| new_expr.match_token != old_expr.match_token
|| new_expr.brace_token != old_expr.brace_token
}

I was unable to reproduce this issue with the CLI version installed from the latest commit on the main branch (9ae3d14) and the fix-fullstack-history branch of dioxus on either the web platform or the fullstack platform on MacOs with this code:

use dioxus::prelude::*;

fn main() {
    launch(app);
}

fn app() -> Element {
    rsx! {
        RecipeView { id: "1".to_string() }
    }
}

#[component]
pub fn RecipeView(id: String) -> Element {
    let recipe: Option<Result<usize, usize>> = Some(Ok(1));
    match recipe {
        Some(Ok(recipe)) => {
            rsx! {
                div { class: "flex flex-col justify-center overflow-hidden py-6",
                    div { class: "mx-auto max-w-2xl",
                        h1 { class: "text-5xl text-center font-extrabold py-3", "{recipe}" }
                        p { class: "text-4xl py-3", "{recipe}" }
                        p { "Another static text with more data" }
                    }
                }
            }
        }
        Some(Err(_)) => {
            rsx! {"Error loading recipe"}
        }
        None => {
            rsx! {"Loading..."}
        }
    }
}

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

Maybe in your code example the compiler optimizes the other cases away, since you have a static value 😊

If I try with DIOXUS_LOG in trace level, I run into another problem which is the hot_reload websocket getting closed early. Also I get this flood of continuous log messages in the terminal that never stop. Keeps the CPU busy

[TRACE] dfs_in_order: visit_instr(Store(Store { memory: Id { idx: 0 }, kind: I32 { atomic: false }, arg: MemArg { align: 4, offset: 4 } }))
[TRACE] dfs_in_order: (Store(Store { memory: Id { idx: 0 }, kind: I32 { atomic: false }, arg: MemArg { align: 4, offset: 4 } })).visit(..)
[TRACE] dfs_in_order: visit_instr(LocalGet(LocalGet { local: Id { idx: 312841 } }))
[TRACE] dfs_in_order: (LocalGet(LocalGet { local: Id { idx: 312841 } })).visit(..)
[TRACE] dfs_in_order: visit_instr(Load(Load { memory: Id { idx: 0 }, kind: I32 { atomic: false }, arg: MemArg { align: 4, offset: 4 } }))
[TRACE] dfs_in_order: (Load(Load { memory: Id { idx: 0 }, kind: I32 { atomic: false }, arg: MemArg { align: 4, offset: 4 } })).visit(..)
[TRACE] dfs_in_order: visit_instr(LocalSet(LocalSet { local: Id { idx: 312856 } }))
[TRACE] dfs_in_order: (LocalSet(LocalSet { local: Id { idx: 312856 } })).visit(..)
[TRACE] dfs_in_order: visit_instr(Const(Const { value: I32(32) }))
[TRACE] dfs_in_order: (Const(Const { value: I32(32) })).visit(..)

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

Actually it seems like those two cases don't ever get to produce a template. When I force a long delay on the server call, I can see in the browser inspector that the component contents for None case is never inserted into the DOM. It just has some <pre hidden></pre> placeholder there.

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

Another weird thing I'm seeing in the logs is the execution of part of the server function multiple times.

[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
spinning up hot reloading
hot reloading ready
🔥 Hot Reload WebSocket connected
Connected to hot reloading 🚀
🔮 Finding updates since last compile...
finished
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved

So the code after the sleep.await gets called five times, seems like once a second when observing.

#[server]
pub async fn get_recipe(id: String) -> Result<Recipe, ServerFnError> {
    println!("get_recipe: {}", id);
    use tokio::time::{sleep, Duration};
    sleep(Duration::from_millis(5000)).await;
    println!("get_recipe: done");
    Ok(Recipe {
        id,
        name: "Test Recipe".to_string(),
        description: "This is a test recipe".to_string(),
        ingredients: vec![],
        steps: vec![],
    })
}

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

BTW didn't see those repeating get_recipe: done with the CLI version (9ae3d14) but still the same hot reloading behavior.

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

Actually it seems like those two cases don't ever get to produce a template. When I force a long delay on the server call, I can see in the browser inspector that the component contents for None case is never inserted into the DOM. It just has some <pre hidden>\</pre\> placeholder there.

The reason for this was the use_server_future. With use_resource it works as expected. Although IMO the behavior should not be like that with the server future when dynamically switching components, instead of doing a full SSR of the page on initial load.

from dioxus.

ealmloff avatar ealmloff commented on May 18, 2024

Another weird thing I'm seeing in the logs is the execution of part of the server function multiple times.

[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
spinning up hot reloading
hot reloading ready
🔥 Hot Reload WebSocket connected
Connected to hot reloading 🚀
🔮 Finding updates since last compile...
finished
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved
get_recipe: done
[INFO] dioxus_core::diff::node - creating template self=VNode { vnode: VNodeInner { key: None, template: Cell { value: Template { name: "src/components/recipe.rs:19:13:2484", roots: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "flex flex-col justify-center overflow-hidden py-6", namespace: None }], children: [Element { tag: "div", namespace: None, attrs: [Static { name: "class", value: "mx-auto max-w-2xl", namespace: None }], children: [Element { tag: "h1", namespace: None, attrs: [Static { name: "class", value: "text-5xl text-center font-extrabold py-3", namespace: None }], children: [DynamicText { id: 0 }] }, Element { tag: "p", namespace: None, attrs: [Static { name: "class", value: "text-xl py-3", namespace: None }], children: [DynamicText { id: 1 }] }, Element { tag: "p", namespace: None, attrs: [], children: [Text { text: "Another static text with more datas" }] }] }] }], node_paths: [[0, 0, 0, 0], [0, 0, 1, 0]], attr_paths: [] } }, dynamic_nodes: [Text(VText { value: "Test Recipe" }), Text(VText { value: "This is a test recipe" })], dynamic_attrs: [] }, mount: Cell { value: MountId(4) } } mount=MountId(4)
[INFO] dioxus_fullstack::render - Suspense resolved

So the code after the sleep.await gets called five times, seems like once a second when observing.

#[server]
pub async fn get_recipe(id: String) -> Result<Recipe, ServerFnError> {
    println!("get_recipe: {}", id);
    use tokio::time::{sleep, Duration};
    sleep(Duration::from_millis(5000)).await;
    println!("get_recipe: done");
    Ok(Recipe {
        id,
        name: "Test Recipe".to_string(),
        description: "This is a test recipe".to_string(),
        ingredients: vec![],
        steps: vec![],
    })
}

Those logs will happen whenever a client makes a request to your server and the server renders the page. If you reload your page 5 times, you should see 5 logs. You may also more logs if an asset is trying to load that doesn't exist and you are rendering a page on a 404 route

from dioxus.

ochrons avatar ochrons commented on May 18, 2024

But I see in the logs only the get_recipe: done message, not the first get_recipe: 1 message, which only occurs once. Couldn't really get this to happen again, so might have been some fluke as well, or some nasty timing issue.

from dioxus.

jkelleyrtp avatar jkelleyrtp commented on May 18, 2024

I think this is a race condition on how we register templates. Templates are given a template ID but that ID assignment can vary depending on the order in which we process them. If the template doesn't exist on the client when we handle its edits, we have nothing to direct those edits to. Hence why you only see edits for the actually loaded block of rsx.

This needs to be solved with more resilient handling of edits queued for nonexistent templates.

from dioxus.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.