I have an async action which alters data in an external api (POST request). When the request has finished, then I need to refetch data from this api, in order to reflect the changes in the view. But when I call my_resource.refetch()
inside the action, I get this warning in the web console:
At .../.cargo/registry/src/github.com-1ecc6299db9ec823/leptos_reactive-0.3.0/src/resource.rs:910:25, you access a signal or memo (defined at .../.cargo/registry/src/github.com-1ecc6299db9ec823/leptos_reactive-0.3.0/src/resource.rs:339:18) outside a reactive tracking context. This might mean your app is not responding to changes in signal values in the way you expect.
How to get rid of it?
Here is a minimal working example which reflects the problem (when clicking on the button, the warning appears):
use std::sync::atomic::{AtomicUsize, Ordering};
use leptos::*;
/// Let's pretend we have some data which can accessed and modified via an external API:
static EXTERNAL_DATA: AtomicUsize = AtomicUsize::new(1);
/// Alters the API data.
pub async fn alter_external_data() {
EXTERNAL_DATA.fetch_add(1, Ordering::SeqCst); // Should be async
}
/// Gets the API data.
pub async fn fetch_external_data(n: i32) -> String {
n.to_string().repeat(EXTERNAL_DATA.load(Ordering::SeqCst)) // Should be async
}
#[component()]
pub fn App(cx: Scope) -> impl IntoView {
let (read_number, set_number) = create_signal(cx, 1);
// `async_data` depends on `read_number`, but must be refreshed each time the button below has
// been clicked, because the external data has been altered.
let async_data = create_local_resource(cx, read_number, |n| async move {
fetch_external_data(n).await
});
// Action which alters the external data. Then `async_data` has to be refetched.
let alter_external_data_action = create_action(cx, move |_: &()| async move {
alter_external_data().await;
async_data.refetch(); // THIS TRIGGERS A WARNING
});
view! { cx,
<p>
"Fetched data: "
{move || async_data.read(cx)}
</p>
<p>
"N = "
<input type="number"
prop:value=read_number
on:change=move |ev| set_number(event_target_value(&ev).parse().unwrap())
/>
</p>
<p>
<button on:click=move |_| alter_external_data_action.dispatch(())>
"Alter external data"
</button>
</p>
}
}
fn main() {
mount_to_body(|cx| view! { cx, <App/> })
}
This can be solved by using an effect which depends on the action's value:
EDIT: This generic method can be used in order to reduce code verbosity:
Now in the solution above, instead of adding the effect manually, you can simply write: