DioxusLabs / DioxusLabs/dioxus
`use_branched_signal` Hook to convert from `ReadOnlySignal` to `Signal`
- Dominant language
- Rust
- Stars
- 39.1k
- Forks
- 1.9k
- Avg merge
- 4d 10h
- Merged PRs (30d)
- 4
Description
## Feature Request
Let's say I want to build a web app where the state is defined this way:
```rust
enum State {
HomeScreen,
MyApp{favorite_food: String}
}
```
Initially, the state is `HomeScreen`, but after the user go through the initial setup, the state is `App{favorite_food: "pasta"}` (let's say the favorite_food comes from a http request). I then want to change the initial "favorite_food" guess if the user changes this information. Conceptually, the code would look like this:
```rust
enum State {
HomeScreen,
App {favorite_food: String}
}
#[component]
fn HomeScreen() -> Element {
todo!()
}
#[component]
fn App(initial_favorite_food: ReadOnlySignal) -> Element {
let mut favorite_food: Signal = todo!();
todo!()
}
#[component]
fn MyApp() -> Element {
let state = use_signal(|| State::HomeScreen);
rsx! {
button {
onclick: move |_| state.set(State::App { favorite_food: "pasta".to_string() })
}
match state.read() {
State::HomeScreen => rsx!{ HomeScreen {} },
State::App{favorite_food: food} => rsx! {App { initial_favorite_food: food }
}
}
}
}
```
The issue is that I don't know how to create a new `Signal` from a `ReadOnlySignal`. This new signal should update both when `initial_relative_food` is updated and when the component `App` changes it (interior mutability)
## Non-solutions
### provide `initial_favorite_food` as a value instead of a signal
It works, but if the `state` signal is updated with a new value, it will not update the component.
This solution also won't work for a non-`Clone` value.
### use `use_effect`
As said in the documentation, "Effects are reactive closures that run after the component has finished rendering". It should not be used to derive state, but I want to derive state in this case.
### use `use_memo`
This would allow to do some computation to `initial_favorite_food` and react to changes, but no interior mutability.
## Implement Suggestion
I think I found a nice way to implement the behaviour I want:
```rust
fn use_branched_signal(mut f: impl FnMut() -> T + 'static) -> Signal {
let location = std::panic::Location::caller();
use_hook(|| {
let (rc, mut changed) = ReactiveContext::new_with_origin(location);
let value = rc.reset_and_run_in(&mut f);
let mut result = Signal::new(value);
spawn_isomorphic(async move {
while changed.next().await.is_some() {
// Remove any pending updates
while changed.try_next().is_ok() {}
let new_value = rc.run_in(&mut f);
result.set(new_value)
}
});
result
})
}
```
To use it, simply do
```rust
#[component]
fn App(initial_favorite_food: ReadOnlySignal) -> Element {
let mut favorite_food: Signal = use_branched_signal(|| initial_favorite_food());
todo!()
}
```
## Other uses
In essence, my proposition is just a new hook that is identical to `use_signal`, but in addition it update when a dependency in the closure changes. I think it would allow a lot of other nice patterns that would otherwise be impossible (or require optional values with unwraps)
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.