leptos-rs / leptos

Build fast web applications with Rust.
https://leptos.dev
MIT License
16.29k stars 650 forks source link

Double navigation cause desync beetween `Location::pathname` and the actual URL #2979

Closed Baptistemontan closed 1 month ago

Baptistemontan commented 1 month ago

Describe the bug A clear and concise description of what the bug is.

Leptos Dependencies

Please copy and paste the Leptos dependencies and features from your Cargo.toml.

For example:

leptos = { version = "0.7.0-beta5", features = ["csr"] }
leptos_router = { version = "0.7.0-beta5"}

To Reproduce

use leptos::prelude::*;
use leptos_router::{
    components::*,
    hooks::{use_location, use_navigate},
    path, NavigateOptions,
};

#[component]
#[allow(non_snake_case)]
pub fn App() -> impl IntoView {
    view! {
        <Router>
            <Routes fallback=|| "This page could not be found.">
                <Route path=path!("/") view=Home />
                <Route path=path!("/counter") view=Counter />
            </Routes>
        </Router>
    }
}

#[component]
#[allow(non_snake_case)]
fn Home() -> impl IntoView {
    let location = use_location();
    let navigate = use_navigate();

    Effect::new(move || {
        if location.pathname.with(|path| path == "/counter") {
            navigate(
                "/",
                NavigateOptions {
                    resolve: false,
                    replace: true,
                    scroll: false,
                    ..Default::default()
                },
            )
        }
    });

    view! {
        <div>{move || location.pathname.get()}</div>
        <A href="/counter">go counter</A>
    }
}

#[component]
#[allow(non_snake_case)]
fn Counter() -> impl IntoView {
    view! {
        <A href="/">go home</A>
    }
}

In this example, when clicking the anchor in the Home component, it navigates to the Counter component at /counter, but the effect trigger another redirect to stay at the Home page, it works as expected, you stay at the Home page, but the url stays /counter. And it also shows that location.pathname is /.

Expected behavior That the url is kept in sync.

Additionnal context It worked fine in 0.6

Baptistemontan commented 1 month ago

Found a work around in the mean time, just wait for the navigation to finish before triggering a new one, did it by waiting for an animation frame:

Effect::new(move || {
    if location.pathname.with(|path| path == "/counter") {
        let navigate = navigate.clone();
        request_animation_frame(move || {
            navigate(
                "/",
                NavigateOptions {
                    resolve: false,
                    replace: true,
                    scroll: false,
                    ..Default::default()
                },
            )
        })
    }
});