Browse Source

Bring back mroe hooks, remove old hooks, cleanup a number of examples

Jonathan Kelley 1 năm trước cách đây
mục cha
commit
74aa55f85f

+ 1 - 0
Cargo.lock

@@ -2631,6 +2631,7 @@ dependencies = [
  "dioxus",
  "dioxus-core",
  "dioxus-debug-cell",
+ "dioxus-signals",
  "futures-channel",
  "futures-util",
  "slab",

+ 3 - 3
examples/compose.rs

@@ -22,7 +22,7 @@ fn app() -> Element {
         dioxus_desktop::window().new_window(
             VirtualDom::new_with_props(compose, tx.clone()),
             Default::default(),
-        )
+        );
     };
 
     rsx! {
@@ -39,7 +39,7 @@ fn app() -> Element {
     }
 }
 
-fn compose(receiver: UnboundedSender<String>) -> Element {
+fn compose(tx: UnboundedSender<String>) -> Element {
     let user_input = use_signal(String::new);
 
     rsx! {
@@ -48,7 +48,7 @@ fn compose(receiver: UnboundedSender<String>) -> Element {
 
             button {
                 onclick: move |_| {
-                    cx.props.app_tx.send(user_input.get().clone());
+                    tx.send(user_input.get().clone());
                     dioxus_desktop::window().close();
                 },
                 "Click to send"

+ 1 - 1
examples/counter.rs

@@ -9,7 +9,7 @@ fn main() {
 
 fn app() -> Element {
     let counters = use_signal(|| vec![0, 0, 0]);
-    let sum = use_selector(move || counters.read().iter().copied().sum() as usize);
+    let sum = use_selector(move || counters.read().iter().copied().sum::<usize>());
 
     render! {
         div {

+ 0 - 7
examples/crm.rs

@@ -24,12 +24,8 @@ pub struct Client {
     pub description: String,
 }
 
-type ClientContext = Vec<Client>;
-
 #[component]
 fn App() -> Element {
-    use_shared_state_provider::<ClientContext>(Default::default);
-
     render! {
         link {
             rel: "stylesheet",
@@ -54,8 +50,6 @@ fn App() -> Element {
 
 #[component]
 fn ClientList() -> Element {
-    let clients = use_shared_state::<ClientContext>().unwrap();
-
     rsx! {
         h2 { "List of Clients" }
         Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" }
@@ -71,7 +65,6 @@ fn ClientList() -> Element {
 
 #[component]
 fn ClientAdd() -> Element {
-    let clients = use_shared_state::<ClientContext>().unwrap();
     let first_name = use_signal(String::new);
     let last_name = use_signal(String::new);
     let description = use_signal(String::new);

+ 21 - 31
examples/dog_app.rs

@@ -5,15 +5,10 @@ fn main() {
     dioxus_desktop::launch(app);
 }
 
-#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
-struct ListBreeds {
-    message: HashMap<String, Vec<String>>,
-}
-
 fn app() -> Element {
     let breed = use_signal(|| "deerhound".to_string());
 
-    let breeds = use_future(|| async move {
+    let breed_list = use_future(|| async move {
         reqwest::get("https://dog.ceo/api/breeds/list/all")
             .await
             .unwrap()
@@ -21,13 +16,13 @@ fn app() -> Element {
             .await
     });
 
-    match breeds.value()? {
-        Ok(breed_list) => rsx! {
+    match breed_list.value().read().as_ref() {
+        Some(Ok(breeds)) => rsx! {
             div { height: "500px",
                 h1 { "Select a dog breed!" }
                 div { display: "flex",
                     ul { flex: "50%",
-                        for cur_breed in breed_list.message.keys().take(10) {
+                        for cur_breed in breeds.message.keys().take(10).cloned() {
                             li { key: "{cur_breed}",
                                 button { onclick: move |_| breed.set(cur_breed.clone()), "{cur_breed}" }
                             }
@@ -37,18 +32,13 @@ fn app() -> Element {
                 }
             }
         },
-        Err(_e) => rsx! { div { "Error fetching breeds" } },
+        _ => rsx! { div { "loading breeds" } },
     }
 }
 
-#[derive(serde::Deserialize, Debug)]
-struct DogApi {
-    message: String,
-}
-
 #[component]
 fn BreedPic(breed: Signal<String>) -> Element {
-    let fut = use_future(|breed| async move {
+    let fut = use_future(|| async move {
         reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
             .await
             .unwrap()
@@ -56,23 +46,23 @@ fn BreedPic(breed: Signal<String>) -> Element {
             .await
     });
 
-    match fut.value()? {
-        Ok(resp) => render! {
+    match fut.value().read().as_ref() {
+        Some(Ok(resp)) => rsx! {
             div {
-                button {
-                    onclick: move |_| {
-                        println!("clicked");
-                        fut.restart()
-                    },
-                    "Click to fetch another doggo"
-                }
-                img {
-                    src: "{resp.message}",
-                    max_width: "500px",
-                    max_height: "500px",
-                }
+                button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
+                img { max_width: "500px", max_height: "500px", src: "{resp.message}" }
             }
         },
-        Err(_) => render! { div { "loading dogs failed" } },
+        _ => rsx! { div { "loading dog picture" } },
     }
 }
+
+#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
+struct ListBreeds {
+    message: HashMap<String, Vec<String>>,
+}
+
+#[derive(serde::Deserialize, Debug)]
+struct DogApi {
+    message: String,
+}

+ 1 - 1
examples/error_handle.rs

@@ -1,7 +1,7 @@
 use dioxus::{core::CapturedError, prelude::*};
 
 fn main() {
-    dioxus_desktop::launch(App);
+    dioxus_desktop::launch(app);
 }
 
 fn app() -> Element {

+ 5 - 9
examples/eval.rs

@@ -5,8 +5,8 @@ fn main() {
 }
 
 fn app() -> Element {
-    let future = use_future(|_| async move {
-        let eval = eval(
+    let future = use_future(|| async move {
+        let mut eval = eval(
             r#"
                 dioxus.send("Hi from JS!");
                 let msg = await dioxus.recv();
@@ -22,12 +22,8 @@ fn app() -> Element {
         res
     });
 
-    match future.value() {
-        Some(v) => rsx!(
-            p { "{v}" }
-        ),
-        _ => rsx!(
-            p { "hello" }
-        ),
+    match future.value().read().as_ref() {
+        Some(v) => rsx!( p { "{v}" } ),
+        _ => rsx!( p { "waiting.." } ),
     }
 }

+ 1 - 1
examples/filedragdrop.rs

@@ -10,7 +10,7 @@ fn main() {
         .launch(app)
 }
 
-fn app(_props: ()) -> Element {
+fn app() -> Element {
     rsx!(
         div {
             h1 { "drag a file here and check your console" }

+ 0 - 152
examples/framework_benchmark.rs

@@ -1,152 +0,0 @@
-#![allow(non_snake_case)]
-
-use dioxus::prelude::*;
-use rand::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-#[derive(Clone, PartialEq)]
-struct Label {
-    key: usize,
-    labels: [&'static str; 3],
-}
-
-impl Label {
-    fn new_list(num: usize) -> Vec<Self> {
-        let mut rng = SmallRng::from_entropy();
-        let mut labels = Vec::with_capacity(num);
-        for x in 0..num {
-            labels.push(Label {
-                key: x,
-                labels: [
-                    ADJECTIVES.choose(&mut rng).unwrap(),
-                    COLOURS.choose(&mut rng).unwrap(),
-                    NOUNS.choose(&mut rng).unwrap(),
-                ],
-            });
-        }
-        labels
-    }
-}
-
-fn app() -> Element {
-    let items = use_signal(Vec::new);
-    let selected = use_signal(|| None);
-
-    rsx! {
-        div { class: "container",
-            div { class: "jumbotron",
-                div { class: "row",
-                    div { class: "col-md-6", h1 { "Dioxus" } }
-                    div { class: "col-md-6",
-                        div { class: "row",
-                            ActionButton { name: "Create 1,000 rows", id: "run",
-                                onclick: move |_| items.set(Label::new_list(1_000)),
-                            }
-                            ActionButton { name: "Create 10,000 rows", id: "runlots",
-                                onclick: move |_| items.set(Label::new_list(10_000)),
-                            }
-                            ActionButton { name: "Append 1,000 rows", id: "add",
-                                onclick: move |_| items.write().extend(Label::new_list(1_000)),
-                            }
-                            ActionButton { name: "Update every 10th row", id: "update",
-                                onclick: move |_| items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"),
-                            }
-                            ActionButton { name: "Clear", id: "clear",
-                                onclick: move |_| items.write().clear(),
-                            }
-                            ActionButton { name: "Swap rows", id: "swaprows",
-                                onclick: move |_| items.write().swap(0, 998),
-                            }
-                        }
-                    }
-                }
-            }
-            table {
-                tbody {
-                    for (id, item) in items.read().iter().enumerate() {
-                        tr {
-                            class: if (*selected).map(|s| s == id).unwrap_or(false) { "danger" },
-                            td { class:"col-md-1" }
-                            td { class:"col-md-1", "{item.key}" }
-                            td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
-                                a { class: "lbl", "{item.labels[0]}{item.labels[1]}{item.labels[2]}" }
-                            }
-                            td { class: "col-md-1",
-                                a { class: "remove", onclick: move |_| { items.write().remove(id); },
-                                    span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" }
-                                }
-                            }
-                            td { class: "col-md-6" }
-                        }
-                    }
-
-                }
-             }
-            span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" }
-        }
-    }
-}
-
-#[derive(Props)]
-struct ActionButtonProps<'a> {
-    name: &'a str,
-    id: &'a str,
-    onclick: EventHandler<'a>,
-}
-
-fn ActionButton(props: ActionButtonProps) -> Element {
-    rsx! {
-        div {
-            class: "col-sm-6 smallpad",
-            button {
-                class:"btn btn-primary btn-block",
-                r#type: "button",
-                id: "{cx.props.id}",
-                onclick: move |_| cx.props.onclick.call(()),
-
-                "{cx.props.name}"
-            }
-        }
-    }
-}
-
-static ADJECTIVES: &[&str] = &[
-    "pretty",
-    "large",
-    "big",
-    "small",
-    "tall",
-    "short",
-    "long",
-    "handsome",
-    "plain",
-    "quaint",
-    "clean",
-    "elegant",
-    "easy",
-    "angry",
-    "crazy",
-    "helpful",
-    "mushy",
-    "odd",
-    "unsightly",
-    "adorable",
-    "important",
-    "inexpensive",
-    "cheap",
-    "expensive",
-    "fancy",
-];
-
-static COLOURS: &[&str] = &[
-    "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
-    "orange",
-];
-
-static NOUNS: &[&str] = &[
-    "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
-    "pizza", "mouse", "keyboard",
-];

+ 1 - 1
examples/openid_connect_demo/src/views/header.rs

@@ -129,7 +129,7 @@ pub fn RefreshToken(cx: Scope<ClientProps>) -> Element {
 
 #[component]
 pub fn LoadClient() -> Element {
-    let init_client_future = use_future(|_| async move { init_oidc_client().await });
+    let init_client_future = use_future(|| async move { init_oidc_client().await });
     let fermi_client: &UseAtomRef<ClientState> = use_atom_ref(&FERMI_CLIENT);
     cx.render(match init_client_future.value() {
         Some(client_props) => match client_props {

+ 7 - 7
examples/optional_props.rs

@@ -35,7 +35,7 @@ fn app() -> Element {
 
 type SthElse<T> = Option<T>;
 
-#[derive(Props, PartialEq)]
+#[derive(Props, PartialEq, Clone)]
 struct ButtonProps {
     a: String,
 
@@ -51,14 +51,14 @@ struct ButtonProps {
     e: SthElse<String>,
 }
 
-fn Button(cx: Scope<ButtonProps>) -> Element {
+fn Button(props: ButtonProps) -> Element {
     rsx! {
         button {
-            "{cx.props.a} | "
-            "{cx.props.b:?} | "
-            "{cx.props.c:?} | "
-            "{cx.props.d:?} | "
-            "{cx.props.e:?}"
+            "{props.a} | "
+            "{props.b:?} | "
+            "{props.c:?} | "
+            "{props.d:?} | "
+            "{props.e:?}"
         }
     }
 }

+ 7 - 10
examples/pattern_model.rs

@@ -120,20 +120,17 @@ fn app() -> Element {
     }
 }
 
-#[derive(Props)]
-struct CalculatorKeyProps {
-    #[props(into)]
-    name: String,
+#[component]
+fn CalculatorKey(
+    #[props(into)] name: String,
     onclick: EventHandler<MouseEvent>,
     children: Element,
-}
-
-fn CalculatorKey(props: CalculatorKeyProps) -> Element {
+) -> Element {
     rsx! {
         button {
-            class: "calculator-key {cx.props.name}",
-            onclick: move |e| cx.props.onclick.call(e),
-            {&cx.props.children}
+            class: "calculator-key {name}",
+            onclick: move |e| onclick.call(e),
+            {&children}
         }
     }
 }

+ 3 - 7
examples/pattern_reducer.rs

@@ -20,13 +20,9 @@ fn app() -> Element {
     rsx!(
         div {
             h1 {"Select an option"}
-            h3 { "The radio is... ", {state.is_playing()}, "!" }
-            button { onclick: move |_| state.make_mut().reduce(PlayerAction::Pause),
-                "Pause"
-            }
-            button { onclick: move |_| state.make_mut().reduce(PlayerAction::Play),
-                "Play"
-            }
+            h3 { "The radio is... ", {state.read().is_playing()}, "!" }
+            button { onclick: move |_| state.write().reduce(PlayerAction::Pause), "Pause" }
+            button { onclick: move |_| state.write().reduce(PlayerAction::Play), "Play" }
         }
     )
 }

+ 1 - 1
examples/read_size.rs

@@ -45,7 +45,7 @@ fn app() -> Element {
             height: "50%",
             background_color: "red",
             onmounted: move |cx| div_element.set(Some(cx.inner().clone())),
-            "This element is {dimensions.read():?}"
+            "This element is {*dimensions():?}"
         }
 
         button {

+ 0 - 73
examples/shared_state.rs

@@ -1,73 +0,0 @@
-use std::collections::HashMap;
-
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(App);
-}
-
-#[derive(Default)]
-struct CoolData {
-    data: HashMap<usize, String>,
-}
-
-impl CoolData {
-    pub fn new(data: HashMap<usize, String>) -> Self {
-        Self { data }
-    }
-
-    pub fn view(&self, id: &usize) -> Option<&String> {
-        self.data.get(id)
-    }
-
-    pub fn set(&mut self, id: usize, data: String) {
-        self.data.insert(id, data);
-    }
-}
-
-#[component]
-#[rustfmt::skip]
-pub fn App() -> Element {
-    use_shared_state_provider(|| CoolData::new(HashMap::from([
-        (0, "Hello, World!".to_string()),
-        (1, "Dioxus is amazing!".to_string())
-    ])));
-
-    render!(
-        DataEditor {
-            id: 0
-        }
-        DataEditor {
-            id: 1
-        }
-        DataView {
-            id: 0
-        }
-        DataView {
-            id: 1
-        }
-    )
-}
-
-#[component]
-fn DataEditor(id: usize) -> Element {
-    let data = use_shared_state::<CoolData>()?;
-
-    render! {
-        p {
-            {data.read().view(id)?}
-        }
-    }
-}
-
-#[component]
-fn DataView(id: usize) -> Element {
-    let data = use_shared_state::<CoolData>()?;
-
-    render! {
-        input {
-            oninput: move |e: FormEvent| data.write().set(*id, e.value()),
-            value: data.read().view(id)?
-        }
-    }
-}

+ 1 - 1
examples/shortcut.rs

@@ -8,7 +8,7 @@ fn main() {
 fn app() -> Element {
     let toggled = use_signal(|| false);
 
-    use_global_shortcut("ctrl+s", move || toggled.toggle());
+    _ = use_global_shortcut("ctrl+s", move || toggled.toggle());
 
     rsx!("toggle: {toggled}")
 }

+ 2 - 2
examples/simple_desktop.rs

@@ -54,13 +54,13 @@ fn NavBar() -> Element {
 
 #[component]
 fn Home() -> Element {
-    log::debug!("rendering home {:?}", cx.scope_id());
+    log::debug!("rendering home {:?}", current_scope_id());
     render! { h1 { "Home" } }
 }
 
 #[component]
 fn BlogList() -> Element {
-    log::debug!("rendering blog list {:?}", cx.scope_id());
+    log::debug!("rendering blog list {:?}", current_scope_id());
     render! { div { "Blog List" } }
 }
 

+ 1 - 1
examples/simple_router.rs

@@ -38,5 +38,5 @@ fn Nav() -> Element {
 }
 
 fn main() {
-    dioxus_desktop::launch(|cx| render!(Router::<Route> {}));
+    dioxus_desktop::launch(|| render!(Router::<Route> {}));
 }

+ 1 - 0
examples/spread.rs

@@ -31,6 +31,7 @@ fn Component(props: Props) -> Element {
 struct Props {
     #[props(extends = GlobalAttributes)]
     attributes: Vec<Attribute>,
+
     extra_data: String,
     extra_data2: String,
 }

+ 1 - 1
examples/ssr.rs

@@ -13,7 +13,7 @@ fn main() {
     // Or we can render rsx! calls themselves
     println!(
         "{}",
-        dioxus_ssr::render_lazy(rsx! {
+        dioxus_ssr::render_element(rsx! {
             div {
                 h1 { "Hello, world!" }
             }

+ 1 - 1
examples/streams.rs

@@ -10,7 +10,7 @@ fn main() {
 fn app() -> Element {
     let count = use_signal(|| 10);
 
-    use_future(|_| async move {
+    use_future(|| async move {
         let mut stream = some_stream();
 
         while let Some(second) = stream.next().await {

+ 2 - 2
examples/suspense.rs

@@ -48,7 +48,7 @@ fn app() -> Element {
 /// Suspense is achieved my moving the future into only the component that
 /// actually renders the data.
 fn Doggo() -> Element {
-    let fut = use_future(|_| async move {
+    let fut = use_future(|| async move {
         #[derive(serde::Deserialize)]
         struct DogApi {
             message: String,
@@ -61,7 +61,7 @@ fn Doggo() -> Element {
             .await
     });
 
-    match fut.value() {
+    match fut.value().read().as_ref() {
         Some(Ok(resp)) => rsx! {
             button {
                 onclick: move |_| fut.restart(),

+ 1 - 1
packages/desktop/examples/stress.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 
 fn app() -> Element {
     let state = use_signal(|| 0);
-    use_future(|_| {
+    use_future(|| {
         to_owned![state];
         async move {
             loop {

+ 1 - 1
packages/dioxus/Cargo.toml

@@ -22,7 +22,7 @@ dioxus-signals = { workspace = true, optional = true }
 dioxus-hot-reload = { workspace = true, optional = true }
 
 [features]
-default = ["macro", "html", "hot-reload", "signals"]
+default = ["macro", "html", "hot-reload", "signals", "hooks"]
 signals = ["dioxus-signals"]
 macro = ["dioxus-core-macro", "dioxus-rsx"]
 html = ["dioxus-html"]

+ 1 - 0
packages/hooks/Cargo.toml

@@ -15,6 +15,7 @@ nightly-features = []
 
 [dependencies]
 dioxus-core = { workspace = true }
+dioxus-signals = { workspace = true }
 futures-channel = { workspace = true }
 tracing = { workspace = true }
 thiserror = { workspace = true }

+ 7 - 23
packages/hooks/src/lib.rs

@@ -55,42 +55,26 @@ macro_rules! to_owned {
     };
 }
 
-// pub mod computed;
-
 mod use_on_destroy;
 pub use use_on_destroy::*;
 
-// mod use_const;
-// pub use use_const::*;
-
 // mod use_context;
 // pub use use_context::*;
 
-// mod use_state;
-// pub use use_state::{use_state, UseState};
-
-// mod use_ref;
-// pub use use_ref::*;
-
-// mod use_shared_state;
-// pub use use_shared_state::*;
+mod use_coroutine;
+pub use use_coroutine::*;
 
-// mod use_coroutine;
-// pub use use_coroutine::*;
-
-// mod use_future;
-// pub use use_future::*;
+mod use_future;
+pub use use_future::*;
 
 // mod use_effect;
 // pub use use_effect::*;
 
-// mod use_callback;
-// pub use use_callback::*;
-
 // mod use_memo;
 // pub use use_memo::*;
 
 // mod use_on_create;
 // pub use use_on_create::*;
-// mod use_root_context;
-// pub use use_root_context::*;
+
+mod use_root_context;
+pub use use_root_context::*;

+ 47 - 71
packages/hooks/src/use_coroutine.rs

@@ -1,4 +1,6 @@
-use dioxus_core::{ScopeState, Task};
+use dioxus_core::prelude::{consume_context, provide_context, push_future, use_hook};
+use dioxus_core::Task;
+use dioxus_signals::{CopyValue, Signal};
 pub use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use std::future::Future;
 
@@ -63,102 +65,76 @@ use std::future::Future;
 ///     }
 /// })
 /// ```
-pub fn use_coroutine<M, G, F>(nit: G) -> &Coroutine<M>
+pub fn use_coroutine<M, G, F>(init: G) -> Coroutine<M>
 where
     M: 'static,
     G: FnOnce(UnboundedReceiver<M>) -> F,
     F: Future<Output = ()> + 'static,
 {
-    cx.use_hook(|| {
+    let coroutine = use_hook(|| {
+        provide_context(Coroutine {
+            needs_regen: Signal::new(true),
+            tx: CopyValue::new(None),
+            task: CopyValue::new(None),
+        })
+    });
+
+    // We do this here so we can capture data with FnOnce
+    // this might not be the best API
+    if *coroutine.needs_regen.read() {
         let (tx, rx) = futures_channel::mpsc::unbounded();
-        let task = cx.push_future(init(rx));
-        cx.provide_context(Coroutine { tx, task })
-    })
+        let task = push_future(init(rx)).unwrap();
+        coroutine.tx.set(Some(tx));
+        coroutine.task.set(Some(task));
+        coroutine.needs_regen.set_untracked(false);
+    }
+
+    coroutine
 }
 
 /// Get a handle to a coroutine higher in the tree
 ///
 /// See the docs for [`use_coroutine`] for more details.
 #[must_use]
-pub fn use_coroutine_handle<M: 'static>() -> Option<&Coroutine<M>> {
-    cx.use_hook(|| cx.consume_context::<Coroutine<M>>())
-        .as_ref()
-}
-
-pub struct Coroutine<T> {
-    tx: UnboundedSender<T>,
-    task: Task,
+pub fn use_coroutine_handle<M: 'static>() -> Option<Coroutine<M>> {
+    use_hook(|| consume_context::<Coroutine<M>>())
 }
 
-// for use in futures
-impl<T> Clone for Coroutine<T> {
-    fn clone(&self) -> Self {
-        Self {
-            tx: self.tx.clone(),
-            task: self.task,
-        }
-    }
+#[derive(PartialEq)]
+pub struct Coroutine<T: 'static> {
+    needs_regen: Signal<bool>,
+    tx: CopyValue<Option<UnboundedSender<T>>>,
+    task: CopyValue<Option<Task>>,
 }
 
 impl<T> Coroutine<T> {
-    /// Get the ID of this coroutine
-    #[must_use]
-    pub fn task_id(&self) -> Task {
-        self.task
+    /// Get the underlying task handle
+    pub fn task(&self) -> Task {
+        self.task.read().clone().unwrap()
     }
 
     /// Send a message to the coroutine
     pub fn send(&self, msg: T) {
-        let _ = self.tx.unbounded_send(msg);
+        let _ = self.tx.read().as_ref().unwrap().unbounded_send(msg);
     }
-}
 
-impl<T> PartialEq for Coroutine<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.task == other.task
+    /// Restart this coroutine
+    ///
+    /// Forces the component to re-render, which will re-invoke the coroutine.
+    pub fn restart(&self) {
+        self.needs_regen.set(true);
+        self.task().stop();
     }
 }
 
-#[cfg(test)]
-mod tests {
-    #![allow(unused)]
-
-    use super::*;
-    use dioxus_core::prelude::*;
-    use futures_channel::mpsc::unbounded;
-    use futures_util::StreamExt;
-
-    fn app(name: String) -> Element {
-        let task = use_coroutine(|mut rx: UnboundedReceiver<i32>| async move {
-            while let Some(msg) = rx.next().await {
-                println!("got message: {msg}");
-            }
-        });
-
-        let task2 = use_coroutine(view_task);
-
-        let task3 = use_coroutine(|rx| complex_task(rx, 10));
-
-        todo!()
-    }
-
-    async fn view_task(mut rx: UnboundedReceiver<i32>) {
-        while let Some(msg) = rx.next().await {
-            println!("got message: {msg}");
-        }
-    }
-
-    enum Actions {
-        CloseAll,
-        OpenAll,
-    }
-
-    async fn complex_task(mut rx: UnboundedReceiver<Actions>, name: i32) {
-        while let Some(msg) = rx.next().await {
-            match msg {
-                Actions::CloseAll => todo!(),
-                Actions::OpenAll => todo!(),
-            }
+// manual impl since deriving doesn't work with generics
+impl<T> Copy for Coroutine<T> {}
+impl<T> Clone for Coroutine<T> {
+    fn clone(&self) -> Self {
+        Self {
+            tx: self.tx,
+            task: self.task,
+            needs_regen: self.needs_regen,
         }
     }
 }

+ 40 - 223
packages/hooks/src/use_future.rs

@@ -1,9 +1,8 @@
 #![allow(missing_docs)]
 use dioxus_core::{ScopeState, Task};
+use dioxus_signals::{use_effect, use_signal, Signal};
 use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc};
 
-use crate::{use_state, UseState};
-
 /// A future that resolves to a value.
 ///
 /// This runs the future only once - though the future may be regenerated
@@ -17,70 +16,29 @@ use crate::{use_state, UseState};
 /// will be canceled before the new one is started.
 ///
 /// - dependencies: a tuple of references to values that are PartialEq + Clone
-pub fn use_future<T, F, D>
-    dependencies: D,
-    future: impl FnOnce(D::Out) -> F,
-) -> &UseFuture<T>
+pub fn use_future<T, F>(future: impl FnMut() -> F) -> UseFuture<T>
 where
     T: 'static,
     F: Future<Output = T> + 'static,
-    D: UseFutureDep,
 {
-    let val = use_signal(|| None);
+    let task = use_signal(|| None);
 
-    let state = cx.use_hook(move || UseFuture {
-        update: cx.schedule_update(),
-        needs_regen: Rc::new(Cell::new(true)),
-        state: val.clone(),
-        task: Default::default(),
+    use_effect(|| {
+        // task.set();
     });
+    //
 
-    let state_dependencies = cx.use_hook(Vec::new);
-
-    if dependencies.clone().apply(state_dependencies) || state.needs_regen.get() {
-        // kill the old one, if it exists
-        if let Some(task) = state.task.take() {
-            cx.remove_future(task);
-        }
-
-        // Create the new future
-        let fut = future(dependencies.out());
-        let val = val.clone();
-        let task = state.task.clone();
-
-        state.task.set(Some(cx.push_future(async move {
-            val.set(Some(fut.await));
-            task.take();
-        })));
-
-        // Mark that we don't need to regenerate
-        state.needs_regen.set(false);
+    UseFuture {
+        value: todo!(),
+        task,
+        state: todo!(),
     }
-
-    // update the current value
-    state.state.current_val = val.current_val.clone();
-
-    state
 }
 
-pub enum FutureState<'a, T> {
-    Pending,
-    Complete(&'a T),
-    Regenerating(&'a T), // the old value
-}
-
-#[derive(Clone)]
 pub struct UseFuture<T: 'static> {
-    update: Arc<dyn Fn()>,
-    needs_regen: Rc<Cell<bool>>,
-    task: Rc<Cell<Option<Task>>>,
-    state: UseState<Option<T>>,
-}
-
-pub enum UseFutureState<'a, T> {
-    Pending,
-    Complete(&'a T),
-    Reloading(&'a T),
+    value: Signal<T>,
+    task: Signal<Option<Task>>,
+    state: Signal<UseFutureState<T>>,
 }
 
 impl<T> UseFuture<T> {
@@ -89,198 +47,57 @@ impl<T> UseFuture<T> {
     /// Will not cancel the previous future, but will ignore any values that it
     /// generates.
     pub fn restart(&self) {
-        self.needs_regen.set(true);
-        (self.update)();
+        // self.needs_regen.set(true);
+        // (self.update)();
     }
 
     /// Forcefully cancel a future
-    pub fn cancel(&self, ) {
-        if let Some(task) = self.task.take() {
-            cx.remove_future(task);
-        }
+    pub fn cancel(&self) {
+        // if let Some(task) = self.task.take() {
+        //     cx.remove_future(task);
+        // }
     }
 
     // Manually set the value in the future slot without starting the future over
     pub fn set(&self, new_value: T) {
-        self.state.set(Some(new_value));
+        // self.state.set(Some(new_value));
     }
 
     /// Return any value, even old values if the future has not yet resolved.
     ///
     /// If the future has never completed, the returned value will be `None`.
-    pub fn value(&self) -> Option<&T> {
-        self.state.current_val.as_ref().as_ref()
+    pub fn value(&self) -> Signal<Option<T>> {
+        todo!()
+        // self.state.current_val.as_ref().as_ref()
     }
 
     /// Get the ID of the future in Dioxus' internal scheduler
     pub fn task(&self) -> Option<Task> {
-        self.task.get()
+        todo!()
+        // self.task.get()
     }
 
     /// Get the current state of the future.
     pub fn state(&self) -> UseFutureState<T> {
-        match (&self.task.get(), &self.value()) {
-            // If we have a task and an existing value, we're reloading
-            (Some(_), Some(val)) => UseFutureState::Reloading(val),
+        todo!()
+        // match (&self.task.get(), &self.value()) {
+        //     // If we have a task and an existing value, we're reloading
+        //     (Some(_), Some(val)) => UseFutureState::Reloading(val),
 
-            // no task, but value - we're done
-            (None, Some(val)) => UseFutureState::Complete(val),
+        //     // no task, but value - we're done
+        //     (None, Some(val)) => UseFutureState::Complete(val),
 
-            // no task, no value - something's wrong? return pending
-            (None, None) => UseFutureState::Pending,
+        //     // no task, no value - something's wrong? return pending
+        //     (None, None) => UseFutureState::Pending,
 
-            // Task, no value - we're still pending
-            (Some(_), None) => UseFutureState::Pending,
-        }
+        //     // Task, no value - we're still pending
+        //     (Some(_), None) => UseFutureState::Pending,
+        // }
     }
 }
 
-pub trait UseFutureDep: Sized + Clone {
-    type Out;
-    fn out(&self) -> Self::Out;
-    fn apply(self, state: &mut Vec<Box<dyn Any>>) -> bool;
-}
-
-impl UseFutureDep for () {
-    type Out = ();
-    fn out(&self) -> Self::Out {}
-    fn apply(self, _state: &mut Vec<Box<dyn Any>>) -> bool {
-        false
-    }
-}
-
-pub trait Dep: 'static + PartialEq + Clone {}
-impl<T> Dep for T where T: 'static + PartialEq + Clone {}
-
-impl<A: Dep> UseFutureDep for &A {
-    type Out = A;
-    fn out(&self) -> Self::Out {
-        (*self).clone()
-    }
-    fn apply(self, state: &mut Vec<Box<dyn Any>>) -> bool {
-        match state.get_mut(0).and_then(|f| f.downcast_mut::<A>()) {
-            Some(val) => {
-                if *val != *self {
-                    *val = self.clone();
-                    return true;
-                }
-            }
-            None => {
-                state.push(Box::new(self.clone()));
-                return true;
-            }
-        }
-        false
-    }
-}
-
-macro_rules! impl_dep {
-    (
-        $($el:ident=$name:ident,)*
-    ) => {
-        impl< $($el),* > UseFutureDep for ($(&$el,)*)
-        where
-            $(
-                $el: Dep
-            ),*
-        {
-            type Out = ($($el,)*);
-
-            fn out(&self) -> Self::Out {
-                let ($($name,)*) = self;
-                ($((*$name).clone(),)*)
-            }
-
-            #[allow(unused)]
-            fn apply(self, state: &mut Vec<Box<dyn Any>>) -> bool {
-                let ($($name,)*) = self;
-                let mut idx = 0;
-                let mut needs_regen = false;
-
-                $(
-                    match state.get_mut(idx).map(|f| f.downcast_mut::<$el>()).flatten() {
-                        Some(val) => {
-                            if *val != *$name {
-                                *val = $name.clone();
-                                needs_regen = true;
-                            }
-                        }
-                        None => {
-                            state.push(Box::new($name.clone()));
-                            needs_regen = true;
-                        }
-                    }
-                    idx += 1;
-                )*
-
-                needs_regen
-            }
-        }
-    };
-}
-
-impl_dep!(A = a,);
-impl_dep!(A = a, B = b,);
-impl_dep!(A = a, B = b, C = c,);
-impl_dep!(A = a, B = b, C = c, D = d,);
-impl_dep!(A = a, B = b, C = c, D = d, E = e,);
-impl_dep!(A = a, B = b, C = c, D = d, E = e, F = f,);
-impl_dep!(A = a, B = b, C = c, D = d, E = e, F = f, G = g,);
-impl_dep!(A = a, B = b, C = c, D = d, E = e, F = f, G = g, H = h,);
-
-/// A helper macro that merges uses the closure syntax to elaborate the dependency array
-#[macro_export]
-macro_rules! use_future {
-    ($cx:ident, || $($rest:tt)*) => { use_future( $cx, (), |_| $($rest)* ) };
-    ($cx:ident, | $($args:tt),* | $($rest:tt)*) => {
-        use_future(
-            $cx,
-            ($($args),*),
-            |($($args),*)| $($rest)*
-        )
-    };
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[allow(unused)]
-    #[test]
-    fn test_use_future() {
-        use dioxus_core::prelude::*;
-
-        struct MyProps {
-            a: String,
-            b: i32,
-            c: i32,
-            d: i32,
-            e: i32,
-        }
-
-        async fn app(cx: Scope<'_, MyProps>) -> Element {
-            // should only ever run once
-            let fut = use_future(|_| async move {});
-
-            // runs when a is changed
-            let fut = use_future((&cx.props.a,), |(a,)| async move {});
-
-            // runs when a or b is changed
-            let fut = use_future((&cx.props.a, &cx.props.b), |(a, b)| async move { 123 });
-
-            let a = use_future(|| async move {
-                // do the thing!
-            });
-
-            let b = &123;
-            let c = &123;
-
-            let a = use_future(|b, c| async move {
-                let a = b + c;
-                let blah = "asd";
-            });
-
-            todo!()
-        }
-    }
+pub enum UseFutureState<T: 'static> {
+    Pending,
+    Complete(Signal<T>),
+    Regenerating(Signal<T>), // the old value
 }

+ 6 - 5
packages/hooks/src/use_root_context.rs

@@ -1,9 +1,10 @@
-use dioxus_core::ScopeState;
+use dioxus_core::{prelude::consume_context, prelude::provide_root_context, use_hook};
 
 ///
-pub fn use_root_context<T: 'static + Clone>(, new: impl FnOnce() -> T) -> &T {
-    cx.use_hook(|| {
-        cx.consume_context::<T>()
-            .unwrap_or_else(|| cx.provide_root_context(new()))
+pub fn use_root_context<T: 'static + Clone>(new: impl FnOnce() -> T) -> T {
+    use_hook(|| {
+        consume_context::<T>()
+            // If no context is provided, create a new one at the root
+            .unwrap_or_else(|| provide_root_context(new()).expect(" A runtime to exist"))
     })
 }

+ 1 - 1
packages/liveview/examples/axum_stress.rs

@@ -3,7 +3,7 @@ use dioxus::prelude::*;
 
 fn app() -> Element {
     let state = use_signal(|| 0);
-    use_future(|_| {
+    use_future(|| {
         to_owned![state];
         async move {
             loop {

+ 3 - 3
packages/router-macro/src/lib.rs

@@ -584,12 +584,12 @@ impl RouteEnum {
         }
 
         quote! {
-            impl dioxus_router::routable::Routable for #name where Self: Clone {
-                const SITE_MAP: &'static [dioxus_router::routable::SiteMapSegment] = &[
+            impl ::dioxus_router::routable::Routable for #name where Self: Clone {
+                const SITE_MAP: &'static [::dioxus_router::routable::SiteMapSegment] = &[
                     #(#site_map,)*
                 ];
 
-                fn render<'a>(&self, cx: &'a dioxus::prelude::ScopeState, level: usize) -> dioxus::prelude::Element {
+                fn render(&self, level: usize) -> ::dioxus::prelude::Element {
                     let myself = self.clone();
                     match (level, myself) {
                         #(#matches)*

+ 1 - 1
packages/signals/src/rt.rs

@@ -158,7 +158,7 @@ impl<T: 'static> CopyValue<T> {
     }
 
     /// Set the value. If the value has been dropped, this will panic.
-    pub fn set(&mut self, value: T) {
+    pub fn set(&self, value: T) {
         *self.write() = value;
     }
 

+ 8 - 0
packages/signals/src/signal.rs

@@ -292,6 +292,14 @@ impl<T: 'static> Signal<T> {
         *self.write() = value;
     }
 
+    /// Set the value of the signal without triggering an update on subscribers.
+    ///
+    /// todo: we should make it so setting while rendering doesn't trigger an update s
+    pub fn set_untracked(&self, value: T) {
+        let mut inner = self.inner.write();
+        inner.value = value;
+    }
+
     /// Run a closure with a reference to the signal's value.
     /// If the signal has been dropped, this will panic.
     #[track_caller]