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

leptos_router parent Route attr view is not generating view for dynamic routes #2586

Closed
vitordhers opened this issue May 10, 2024 · 4 comments

Comments

@vitordhers
Copy link

vitordhers commented May 10, 2024

Describe the bug
When trying to render a transparent component, the parent Router view doesn't get rendered if I try to it dynamically. As a matter of fact, not even when trying to use static content it gets rendered.

#[component(transparent)]
fn TestTransparent() -> impl IntoView {
    view! {
        <Route path="/" view=|| view! {<div> <p>this is useless</p> <Outlet /> </div>}>
            <Route path="test" view =|| view! {<span>TEST CHILD #2</span>} />
            <Route path="" view =|| view! {<span>TEST CHILD #1</span>} />
        </Route>
    }
}

so, when I proceed to log!("TEST TRANSPARENT {:?}", view! {<TestTransparent />}.render_to_string());, I get an empty string, as if not even the parent component Route view has been rendered.
I believe that's a bug, since at least the view on the parent Route should be rendered, regardless of child routes.

Leptos Dependencies

actix-files = { version = "0.6", optional = true }
actix-web = { version = "4", optional = true, features = ["macros"] }
http = { version = "1.0.0", optional = true }
leptos_actix = { version = "0.6.11", optional = true }
leptos_i18n = { workspace = true, features = ["track_locale_files", "serde"] }
leptos ={ version = "0.6.11", features = [] }
leptos_meta = { version = "0.6.11", features = [] }
leptos_router = { version = "0.6.11", features = [] }
wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] }
wasm-bindgen-futures = { version = "0.4" }
js-sys = "0.3"
serde = { version = "1.0.197", features = ["derive"] }
serde-wasm-bindgen = "0.6"
console_error_panic_hook = "0.1.7"

To Reproduce
Steps to reproduce the behavior:

  1. Go to 'My repository' in order to reproduce the issue;
  2. run cargo leptos watch in order for the code to build and compile;
  3. Go to client/src/pages/locale_routes/mod.rs in order to see where the logs come from
  4. See error

Expected behavior
render_to_string should display nested components in transparent routes, regardless of dynamic or static

Additional context
My application intends to make heavy use of i18n features for displaying appropriate ssr results and custom route names, to the point it makes no sense to write each one manually.

Thanks for your awesome work!

@gbj
Copy link
Collaborator

gbj commented May 10, 2024

I'm not entirely sure what your example is supposed to show or how it relates to the problem in the application.

In particular, it seems like your discussion of .render_to_string() is an XY problem; .render_to_string() shouldn't be displaying anything in the case of these transparent Route components, which are intended just to build a RouteDefinition. It seems like maybe you're trying to use render_to_string() for debugging purposes here (right?) and it's not working as expected, but that that's not the actual issue.

With very complex or dynamic routing setups like the one you're trying to create, I would suggest ditching the <Route/> component etc. and just building a nested RouteDefinition struct manually. This is what <Route/> returns in any case, and the reason things are marked #[component(transparent)] is to allow them just to return that RouteDefinition.

Alternately, if you provide a minimal reproduction (not your entire application) that illustrates the issue and try to explain more of the expected vs actual behavior I can try to help figure it out.

@vitordhers
Copy link
Author

vitordhers commented May 15, 2024

Yes, as you correctly noticed, I was using render_to_string() in order to debug the components inside routes.
But apparently, my issue appears when trying to consolidate components into views and nesting them.

Differently from what I thought, the problem doesn't come from having components between nested Routes (once they are marked as #component(transparent)), but from both trying to collect_into_view them, and from converting them into a Fragment (and then proceeding to map it to view).

Since both approaches mentioned above yield the infamous component with id #-#-#-# not found, ignoring it for hydration error, and as mentioned at Hydration Errors chapter from Leptos book, I felt free to inquire further on this matter.

Please feel free take a look on this minimal reproduction repository, as I believe it illustrates the issue better. So, as you can infer, the expected behavior is to have all 4 components routes listed and accessible in the application when iterating (or casting into Fragment) the DeepNestedRouteSelector component.

I've seen your suggestion of implementing the RouteDefinition manually, but for me, it didn't look so simple to do, specially when it comes on having a concise id for ensuring correct hydration. I've also noticed that you're considering changing visibility on new_route_id function (which could help in my case, I assume), so I'd like to request, if possible -- could you please provide me with an example of implementing RouteDefinition manually?

Finally, thanks for your effort on developing Leptos, it's a great framework, indeed.

@gbj
Copy link
Collaborator

gbj commented May 15, 2024

The fundamental issue here is that children doesn't flatten the children you pass it. Route expects to get n children, each of which is a Route. In your enumerated view, you pass it 2 children, each of which is a Route. In your collected and fragment views, you pass it 1 child, which is a Vec or Fragment. These are not flattened.

This just has to do with the way the view macro constructs children. Here's a working version of the collected approach that I created by expanding the macro for the working enumerated version:

#[component(transparent)]
fn CollectedNestedRoutes() -> impl IntoView {
    let nested = Route(
        RouteProps::builder()
            .path("/")
            .view(SimpleNavComponent)
            // this is very similar to the way the view macro constructs children, 
            // but allows you to construct the vec however you'd like rather than just adding each child
            .children(ToChildren::to_children(|| {
                Fragment::lazy(|| {
                    vec![1, 2]
                        .into_iter()
                        .map(|n: i8| view! { <DeepNestedRouteSelector url=n /> })
                        .collect()
                })
            }))
            .build(),
    );
    view! {
        <Route path="/" view=|| view! {<Html lang="en-US" /><Outlet />}>
            {nested}
            <Route path="/comp3" view=SimpleComponent3 />
            <Route path="/comp4" view=SimpleComponent4 />
        </Route>
    }
}

In case it's helpful in reducing boilerplate, by the way, any component with no props is just a function with no arguments -- so you can replace every instance of something like view=|| view! { <SimpleNavComponent/> } with just view=SimpleNavComponent.

@vitordhers
Copy link
Author

Greg, it really did the trick, thanks a lot.

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

No branches or pull requests

2 participants