Route back to current index, Yew.rs

Dear Rust community.
I successfully navigate to my page depending on the current_video_index. I would like help / suggestions of how to navigate back to MainMenu AND land in the same index.

Use-case example: From MainMenu I'm getting displayed demo video 2, and Route to AboutChoreo2. When I navigate back to MainMenu I want to be presented with demo video 2 again (now it defaults back to index 0, aka demo video 1)

#[function_component(MainMenu)]
pub fn main_menu() -> Html {
    let demo_videos = get_demo_videos();
    // State to track the index of the currently displayed demo video
    let current_video_index = use_state(|| 0);
    let navigator = use_navigator();
    
    pub fn navigate_to_about(index: usize, navigator: Option<Navigator>) -> usize {
        if let Some(navigator) = navigator {
            match index {
                0 => navigator.push(&Route::AboutChoreo1),
                1 => navigator.push(&Route::AboutChoreo2),
                2 => navigator.push(&Route::AboutChoreo3),
                3 => navigator.push(&Route::AboutChoreo4),
                _ => {}
            }
        } else {
            log!("Navigator is None");
        }
        index
    }
    
    let handle_keydown_toggle =
        get_toggle_key(&demo_videos, current_video_index.clone());

        let current_video_index_clone = current_video_index.clone();
        let press_r_for_about = Callback::from(move |event: KeyboardEvent| {
            if event.key() == "r" {
                navigate_to_about(*current_video_index_clone, navigator.clone());
                
                let soundeffect = web_sys::HtmlAudioElement::new_with_src("static/buttonClick.mp3").unwrap();
                let _ = soundeffect.play();
            }
        });

    let navigator = use_navigator().unwrap();
    let restart_app = Callback::from(move |event: KeyboardEvent| {
        if event.key() == "q" {
            navigator.push(&Route::IntroScreen1);
        }
    });

    html! {
        <div onkeydown={restart_app} onkeydown={press_r_for_about} tabindex="0">
            <audio src={format!("static/8bit-menusong-short-ed.aif")} autoplay=true loop=true />
            <div onkeydown={handle_keydown_toggle} tabindex="0">
                <VideosList videos={demo_videos} current_index={*current_video_index} />
                <img src="static/danceOmatic_logo.png" alt="logo of danceomatic"/>
            </div>
        </div>
    }
}

Code for About Choreo:

use yew::prelude::*;
use crate::Route;
use yew_router::prelude::use_navigator;



#[function_component(AboutChoreo1)]
pub fn about_choreo1() -> Html {
    let navigator = use_navigator().unwrap();
    let event_key = Callback::from(move |event: KeyboardEvent| {
        if event.key() == "q"{
        navigator.push(&Route::IntroScreen1);
        } else if event.key() == "r" {
            navigator.push_with_state(&Route::MainMenu, 0);        
        }});

    
   
    html! { 
        <div onkeydown={event_key} tabindex="0">
            <p>{ "Choreo1" }</p>
        </div>
    }
}

#[function_component(AboutChoreo2)]
pub fn about_choreo2() -> Html {
    let navigator = use_navigator().unwrap();
    let event_key = Callback::from(move |event: KeyboardEvent| {
        if event.key() == "q" {
            navigator.push(&Route::IntroScreen1);
        } else if event.key() == "r" {
            navigator.push_with_state(&Route::MainMenu, 1); // Navigate back to MainMenu with index 2
        }
    });

    html! {
        <div onkeydown={event_key} tabindex="1">
            <p>{ "Choreo2" }</p>
        </div>
    }
}

// similar code for #[function_component(AboutChoreo3)] & #[function_component(AboutChoreo4)]

As you can see I tried to push_with_state, and also tried impl. that in #[function_component(MainMenu)]. No success.

Link to repo: https://github.com/martinschultzkristensen/yew-web-app/tree/returnToMain

Thank you for helping me!

AFAICT, you have to access the state through Location::state in your main menu. You can use the use_location hook to retrieve the right location and retrieve the associated state.

Thanks for the reply @jofas .
I'm I on the right track if I try to cast of a pub fn path(&self) -> &str

Returns the pathname of current location, or do you suggest I go for another function (for instance: query or state)

So many options. :slight_smile:

I thought maybe it'd be as easy as:

#[function_component(MainMenu)]
pub fn main_menu() -> Html {
    let demo_videos = get_demo_videos();
    // State to track the index of the currently displayed demo video
-    let current_video_index = use_state(|| 0);
+    let current_video_index = use_location().map(|l| l.state::<usize>().map(|i| *i)).flatten().unwrap_or(0);

using Location::state.

Hey @jofas
Thanks for the competent suggestion. I appreciate it very much.

I spend some time wrapping my head around the line of code, and had some tries. It is of type usize, where the old one is of type UseStateHandle <i32>.

//New line of code:
let current_video_index: usize = use_location().map(|l| l.state::<usize>().map(|i| *i)).flatten().unwrap_or(0);

//previous line of code:
let current_video_index: UseStateHandle<i32> = use_state(|| 0);

I got the code to compile but the fn get_toggle_key doesn't capture the current_video_index.

you can see it here: github

Is there a way to get the new line of code a UseStateHandle<i32> ?

Ah yes, I missed that you probably need to change the index and not only receive it via routing. In that case I'd just combine both lines. If the index is provided via routing (through Location), we use that and if not we default to 0 (which your use_state(|| 0) currently does) and wrap that index in a state handle so we can change it in a way that causes the UI to respond:

let current_video_index: i32 = use_location().map(|l| l.state::<i32>().map(|i| *i)).flatten().unwrap_or(0);
let current_video_index: UseStateHandle<i32> = use_state(|| current_video_index);

That worked well.
Easy to implement without errors. However I still don't manage to navigate back to the state. Hope you're still up for helping me. :sweat_smile:

I can narrow it down to these two sections of code:
I need to somehow caputer the state so the press of "r" will lead me back to the exact index I'm coming form.

// in video_list.rs
#[function_component(VideosList)]
pub fn videos_list(
    VideosListProps {
        videos,
        current_index,
    }: 
    &VideosListProps,) -> Html {
    // Use the current_index to display the corresponding video
    let current_video = &videos[*current_index];
// in about_choreo1.rs
#[function_component(AboutChoreo1)]
pub fn about_choreo1() -> Html {
    let navigator = use_navigator().unwrap();
    let event_key = Callback::from(move |event: KeyboardEvent| {
        if event.key() == "q"{
        navigator.push(&Route::IntroScreen1);
        } else if event.key() == "r" {
            navigator.push_with_state(&Route::MainMenu, 0);        
        }});

I tried to push it to here: github

Type inference needs to be guided here a little:

- navigator.push_with_state(&Route::MainMenu, 0);    
+ navigator.push_with_state(&Route::MainMenu, 0usize);

I PRed the necessary changes.


As a side node: when sharing your project with the community, please run clippy (cargo clippy) and rustfmt (cargo fmt) on your code base, it makes it a lot easier for outsiders to debug your code if it adheres to the rules of idiomatic and well-formatted Rust code.

1 Like

Just a quick response, and then I'll write a comprehensive conclusion to the topic and add the solution result, later today.

I just tested it and It works perfect. I'm so happy for your assistance. :seedling:
You side node is much appreciated, and I'll definitely do my best to apply to the rules of idiomatic and well-formatted Rust code. I still have so much to learn. :disguised_face:

1 Like

Solution found!
Objective: When Routing from 'MainMenu' a current index corresponds to an about_site (ie from index1 Route to about_index1, from index2 Route to about_index2 ect.) the issue was to Route back to MainMenu, landing at the same index.

Previous a vec of videos were tracked by

let current_video_index = use_state(|| 0); // <-- old line of code

To fetch the video index from the location state, instead of directly initializes the state with the default value of 0 we needed these lines of code:

let current_video_index: usize = use_location()
        .and_then(|l| l.state::<usize>().map(|i| *i))
        .unwrap_or(0);

//usize impl. copy trade 
let current_video_index: UseStateHandle<usize> = use_state(|| current_video_index);
  • The state is initialized with the value of current_video_index. This means that the state will initially hold the value retrieved from the location state, ensuring consistency between the location state and the application state.

Next was to edit the code in about_index

#[function_component(AboutIndex2)] 
pub fn about_index2() -> Html {
    let navigator = use_navigator().unwrap();
    let event_key = Callback::from(move |event: KeyboardEvent| {
        if event.key() == "q" {
            navigator.push(&Route::IntroScreen1);
        } else if event.key() == "r" {
            navigator.push_with_state(&Route::MainMenu, 1usize); // Navigate back to MainMenu with index 2
        }
    });

    html! {
        <div onkeydown={event_key} tabindex="1">
            <p>{ "Choreo2" }</p>
        </div>
    }
}

Thanks for all the assistance.
Full code can be found @ previous provided GitHub link
:sunflower: