0

This sounds like something very basic to implement but somehow I cannot figure it out. I'm building a website that shows some data on a page. Since there is a lot of data I want to display it in a paginated way. What I'm struggling with is the state management. Let me first show the code

I have a route with a path segment:

#[derive(Clone, Routable, PartialEq)]
pub enum Route {
    #[at("/:token_address")]
    Collection { token_address: String },
}

Then I have 4 different Collections (different token_address) in the header component (see the gif below) And I have the Collection function_component itself:

#[derive(Properties, PartialEq)]
pub struct Props {
    pub token_address: String,
}

#[function_component(Collection)]
pub fn collection_function_component(props: &Props) -> Html {
    let page = use_state(|| 1);
    let mint = use_state(|| None);
    {
        let token_address = props.token_address.clone();
        let mint = mint.clone();
        let page_val = *page;
        info!("page_val {} token {}", page_val, token_address);
        use_effect_with_deps(
            move |_| {
                wasm_bindgen_futures::spawn_local(async move {
                    match api_utils::fetch_single_api_response::<MintData>(
                        format!("/mint/mints?token_address={}&page={}", token_address, page_val).as_str(),
                    ).await
                    {
                        Ok(fetched_mint) => {
                            mint.set(Some(fetched_mint));
                        }
                        Err(e) => {
                            error!("{e}")
                        }
                    }
                });
            },
            (page_val, props.token_address.clone()),
        );
    }
    // mint and page response handling logic
}

I omitted the mint and page response handling logic, but inside there I update the page state whenever the pagination buttons are pressed. No update to the mint state is performed. It works, but there are two issues with this:

  1. When a different page is pressed the old data is not removed as such. So, it looks like this ezgif com-optimize

  2. Whenever I switch between different Collection token_addresses the page state is not reset, the logs are showing that as well

page_val 3 token 0x9e0d99b864e1ac12565125c5a82b59adea5a09cd
page_val 3 token 0x844a2a2b4c139815c1da4bdd447ab558bb9a7d24
page_val 3 token 0xc1f1da534e227489d617cd742481fd5a23f6a003
page_val 3 token 0x9e0d99b864e1ac12565125c5a82b59adea5a09cd

I've seen the Caution statement about using use_effect_with_deps and use_state, but I do have the page_val as dependent already. I thought I should use TearDown of use_effect_with_deps to reset both states but it doesn't work

move || {
           page.set(1);
           mint.set(None);
        }
  1. for page it keeps the actual value at 1 regardless of what was previously set
  2. for mint it doesn't compile because of async move to the REST API call

what I am doing wrong?

Enigo
  • 3,685
  • 5
  • 29
  • 54
  • How did you go about this? – collinsmarra Jun 15 '23 at 19:01
  • @collinsmarra so far no solution – Enigo Jun 16 '23 at 06:20
  • you can have a look at this https://github.com/ctron/patternfly-yew/blob/main/src/components/pagination.rs. This is how the output looks https://ctron.github.io/patternfly-yew-quickstart/component/pagination. if the second URL returns 404 try accessing it from the root. I hope could help – collinsmarra Jun 16 '23 at 06:27
  • I finally got some time to look into this and @collinsmarra I'm not sure which part exactly to look at in the project you provided – Enigo Aug 20 '23 at 05:14

1 Answers1

0

An acceptable solution was actually quite simple - set the state to None before querying a new one, like this:

#[function_component(Collection)]
pub fn collection_function_component(props: &Props) -> Html {
    let page = use_state(|| 1);
    let mint = use_state(|| None);
    {
        let token_address = props.token_address.clone();
        let mint = mint.clone();
        let page_val = *page;
        info!("page_val {} token {}", page_val, token_address);
        use_effect_with_deps(
            move |_| {

                mint.set(None) // <-- added this

                wasm_bindgen_futures::spawn_local(async move {
                    match api_utils::fetch_single_api_response::<MintData>(
                        format!("/mint/mints?token_address={}&page={}", token_address, page_val).as_str(),
                    ).await
                    {
                        Ok(fetched_mint) => {
                            mint.set(Some(fetched_mint));
                        }
                        Err(e) => {
                            error!("{e}")
                        }
                    }
                });
            },
            (page_val, props.token_address.clone()),
        );
    }
}

With that the state is reset and the view is re-rendered with None state, so you would need to make sure you have something rendered there, e.g.:

    return match (*mint).as_ref() {
        Some(mint) => {
            html! {
              // render mints
            }    
        }
        None => {
            html! {
                // add a loading spinner, for example
            }
        }
    };

P.S. I omitted the page related logic as it was reworked in my code and no longer needed. But the same approach can be applied there as well.

Enigo
  • 3,685
  • 5
  • 29
  • 54