pub fn spawn_isomorphic(fut: impl Future<Output = ()> + 'static) -> Task
Expand description
Start a new future on the same thread as the rest of the VirtualDom.
You should generally use spawn
instead of this method unless you specifically need to run a task during suspense
This future will not contribute to suspense resolving but it will run during suspense.
Because this future runs during suspense, you need to be careful to work with hydration. It is not recommended to do any async IO work in this future, as it can easily cause hydration issues. However, you can use isomorphic tasks to do work that can be consistently replicated on the server and client like logging or responding to state changes.
// ❌ Do not do requests in isomorphic tasks. It may resolve at a different time on the server and client, causing hydration issues.
let mut state = use_signal(|| None);
spawn_isomorphic(async move {
state.set(Some(reqwest::get("https://api.example.com").await));
});
// ✅ You may wait for a signal to change and then log it
let mut state = use_signal(|| 0);
spawn_isomorphic(async move {
loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
println!("State is {state}");
}
});
§Compiler errors you may run into while using spawn
async block may outlive the current function, but it borrows `value`, which is owned by the current function
Tasks in Dioxus need only access data that can last for the entire lifetime of the application. That generally means data that is moved into the async block. If you get this error, you may have forgotten to add move
to your async block.
Broken component:
use dioxus::prelude::*;
fn App() -> Element {
let signal = use_signal(|| 0);
use_hook(move || {
// ❌ The task may run at any point and reads the value of the signal, but the signal is dropped at the end of the function
spawn(async {
println!("{}", signal());
})
});
todo!()
}
Fixed component:
use dioxus::prelude::*;
fn App() -> Element {
let signal = use_signal(|| 0);
use_hook(move || {
// ✅ The `move` keyword tells rust it can move the `state` signal into the async block. Since the async block owns the signal state, it can read it even after the function returns
spawn(async move {
println!("{}", signal());
})
});
todo!()
}
use of moved value: `value`. move occurs because `value` has type `YourType`, which does not implement the `Copy` trait
Data in rust has a single owner. If you run into this error, you have likely tried to move data that isn’t Copy
into two different async tasks. You can fix this issue by making your data Copy
or calling clone
on it before you move it into the async block.
Broken component:
// `MyComponent` accepts a string which cannot be copied implicitly
#[component]
fn MyComponent(string: String) -> Element {
use_hook(move || {
// ❌ We are moving the string into the async task which means we can't access it elsewhere
spawn(async move {
println!("{}", string);
});
// ❌ Since we already moved the string, we can't move it into our new task. This will cause a compiler error
spawn(async move {
println!("{}", string);
})
});
todo!()
}
You can fix this issue by either:
- Making your data
Copy
withReadOnlySignal
:
// `MyComponent` accepts `ReadOnlySignal<String>` which implements `Copy`
#[component]
fn MyComponent(string: ReadOnlySignal<String>) -> Element {
use_hook(move || {
// ✅ Because the `string` signal is `Copy`, we can copy it into the async task while still having access to it elsewhere
spawn(async move {
println!("{}", string);
});
// ✅ Since `string` is `Copy`, we can copy it into another async task
spawn(async move {
println!("{}", string);
})
});
todo!()
}
- Calling
clone
on your data before you move it into the closure:
// `MyComponent` accepts a string which doesn't implement `Copy`
#[component]
fn MyComponent(string: String) -> Element {
use_hook(move || {
// ✅ The string only has one owner. We could move it into this closure, but since we want to use the string in other closures later, we will clone it instead
spawn({
// Clone the string in a new block
let string = string.clone();
// Then move the cloned string into the async block
async move {
println!("{}", string);
}
});
// ✅ We don't use the string after this closure, so we can just move it into the closure directly
spawn(async move {
println!("{}", string);
})
});
todo!()
}