I'm currently working on a web application using the Yew framework, and I'm facing challenges in managing asynchronous tasks for real-time updates. The application involves displaying a dynamic list of items from a source. I want the application to work real-time. So I have set an interval between which I request for the latest value from the given source.
Also, the source of the items may be changed at any time when the callback updatedirectory is invoked.
#[function_component]
pub fn Pane() -> Html {
let ApplicationContext {theme,sizes,settings} = use_context().unwrap();
let directory = use_state(|| crate::app::data::directory());
let entries = use_state(|| Entries::new());
{
let entries = entries.clone();
let directory = directory.clone();
use_effect(move || {
yew::platform::spawn_local(async move {
sleep(settings.refresh_wait()).await;
/// some backend calls and setting of latest entries
});
})
}
let updatedirectory = Callback::from({
let directory = directory.clone();
move |_| {
// sets a new state for the directory
}
});
// entries is rendered here
}
Here are the problems with this code observed when updatedirectory is invoked
- The asynchronous tasks inside the
spawn_localseem to pile up. When state changes, previous async tasks are not dropped and persist alongside the newest use_effect - Unexpected behavior where items from different sources (directories) are displayed interchangeably, this may have been a side-effect of the former.
If only I could cancel the spawned task, I may not have any problems.
#[function_component]
pub fn Pane() -> Html {
let ApplicationContext {theme,sizes,settings} = use_context().unwrap();
let directory = use_state(|| crate::app::data::directory());
let entries = use_state(|| Entries::new());
let aborthandle = use_mut_ref(|| None);
{
let entries = entries.clone();
let directory = directory.clone();
let aborthandle = aborthandle.clone();
use_effect(move || {
let newhandle = tokio::task::spawn_local(async move {
sleep(settings.refresh_wait()).await;
/// some backend calls and setting of entries
});
if let Some(oldhandle) = aborthandle.borrow_mut().replace(newhandle) {
oldhandle.abort();
}
})
}
let updatedirectory = Callback::from({
let dir= directory.clone(); let entries = entries.clone();
move |nd| {
dir.set(nd);
entries.set(Entries::new);
}
});
// entries rendered here
}
But this runs into the runtime error:
panicked at '`spawn_local` called from outside of a `task::LocalSet`', src\app\cview\pane\mod.rs:34:30
Why do I get this error when yew::platform::spawn_local() also involves a call to tokio::task::spawn_local anyways?
How can I work around this?
If it is not plausible to use use_effect, what is a better way to achieve the desired functionality?