Forráskód Böngészése

docs: big updates to the reference

Jonathan Kelley 4 éve
szülő
commit
583fdfa
57 módosított fájl, 724 hozzáadás és 245 törlés
  1. 2 1
      Cargo.toml
  2. 1 1
      docs/main-concepts/03-rsx.md
  3. 1 1
      docs/platforms/06-components.md
  4. 1 1
      examples/_examples/context.rs
  5. 1 1
      examples/_examples/hello.rs
  6. 2 2
      examples/_examples/infer.rs
  7. 1 1
      examples/_examples/input.rs
  8. 1 1
      examples/_examples/jackjill.rs
  9. 1 1
      examples/_examples/jackjillrsx.rs
  10. 1 1
      examples/_examples/many.rs
  11. 1 1
      examples/_examples/rsxt.rs
  12. 1 1
      examples/doggo.rs
  13. 2 2
      examples/examples/demo.rs
  14. 2 6
      examples/model.rs
  15. 7 2
      examples/reference/antipatterns.rs
  16. 0 10
      examples/reference/async.rs
  17. 29 2
      examples/reference/basics.rs
  18. 2 3
      examples/reference/children.rs
  19. 33 9
      examples/reference/conditional_rendering.rs
  20. 28 1
      examples/reference/controlled_inputs.rs
  21. 37 3
      examples/reference/custom_elements.rs
  22. 1 3
      examples/reference/empty.rs
  23. 11 6
      examples/reference/fragments.rs
  24. 1 2
      examples/reference/global_css.rs
  25. 31 3
      examples/reference/inline_styles.rs
  26. 7 7
      examples/reference/iterators.rs
  27. 7 1
      examples/reference/listener.rs
  28. 165 2
      examples/reference/main.rs
  29. 8 9
      examples/reference/memo.rs
  30. 1 1
      examples/reference/noderefs.rs
  31. 1 1
      examples/reference/signals.rs
  32. 4 5
      examples/reference/spreadpattern.rs
  33. 1 1
      examples/reference/statemanagement.rs
  34. 11 42
      examples/reference/suspense.rs
  35. 61 0
      examples/reference/task.rs
  36. 1 2
      examples/reference/testing.rs
  37. 18 3
      examples/reference/tostring.rs
  38. 0 26
      examples/reference/webcomponents.rs
  39. 1 1
      examples/rsx_usage.rs
  40. 6 6
      examples/tailwind.rs
  41. 114 0
      examples/todomvc.rs
  42. 16 16
      notes/Parity.md
  43. 2 2
      notes/SOLVEDPROBLEMS.md
  44. 8 22
      packages/core-macro/src/rsx/ambiguous.rs
  45. 2 2
      packages/core-macro/src/rsx/component.rs
  46. 0 5
      packages/core-macro/src/rsx/element.rs
  47. 1 1
      packages/core/examples/alternative.rs
  48. 1 5
      packages/core/examples/contextapi.rs
  49. 13 2
      packages/core/src/context.rs
  50. 1 1
      packages/core/src/nodes.rs
  51. 53 0
      packages/hooks/src/usestate.rs
  52. 3 0
      packages/html/src/lib.rs
  53. 3 2
      packages/mobile/src/lib.rs
  54. 1 1
      packages/ssr/examples/tide.rs
  55. 6 6
      packages/ssr/examples/tofile.rs
  56. 6 6
      packages/web/examples/demo.rs
  57. 4 2
      src/lib.rs

+ 2 - 1
Cargo.toml

@@ -19,7 +19,7 @@ dioxus-mobile = { path = "./packages/mobile", optional = true }
 
 [features]
 # core
-default = ["core"]
+default = ["core", "ssr"]
 core = ["macro", "hooks", "html"]
 macro = ["dioxus-core-macro"]
 hooks = ["dioxus-hooks"]
@@ -48,6 +48,7 @@ async-std = { version = "1.9.0", features = ["attributes"] }
 im-rc = "15.0.0"
 rand = { version = "0.8.4", features = ["small_rng"] }
 fxhash = "0.2.1"
+gloo-timers = "0.2.1"
 
 [workspace]
 members = [

+ 1 - 1
docs/main-concepts/03-rsx.md

@@ -60,7 +60,7 @@ Commas are entirely optional, but might be useful to delineate between elements
 The `render` function provides an **extremely efficient** allocator for VNodes and text, so try not to use the `format!` macro in your components. Rust's default `ToString` methods pass through the global allocator, but all text in components is allocated inside a manually-managed Bump arena. To push you in the right direction, all text-based attributes take `std::fmt::Arguments` directly, so you'll want to reach for `format_args!` when the built-in `f-string` interpolation just doesn't cut it.
 
 ```rust
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
 
     let text = "example";
 

+ 1 - 1
docs/platforms/06-components.md

@@ -25,7 +25,7 @@ fn Example(cx: Context, name: String) -> VNode {
 // or
 
 #[functional_component]
-static Example: FC = |cx, name: String| html! { <div> "Hello {name}!" </div> };
+pub static Example: FC = |cx, name: String| html! { <div> "Hello {name}!" </div> };
 ```
 
 The final output of components must be a tree of VNodes. We provide an html macro for using JSX-style syntax to write these, though, you could use any macro, DSL, templating engine, or the constructors directly.

+ 1 - 1
examples/_examples/context.rs

@@ -25,7 +25,7 @@ fn main() {
 #[derive(Debug)]
 struct CustomContext([&'static str; 3]);
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.use_create_context(|| CustomContext(["Jack", "Jill", "Bob"]));
 
     cx.render(rsx! {

+ 1 - 1
examples/_examples/hello.rs

@@ -13,7 +13,7 @@ fn main() {
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
 }
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let nodes = (0..500).map(|f| rsx! (li {"div"}));
     cx.render(rsx! {
         div {

+ 2 - 2
examples/_examples/infer.rs

@@ -14,7 +14,7 @@ fn main() {
 }
 
 // this is a component
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let (event, set_event) = use_state_classic(cx, || None);
 
     let handler = move |evt| {
@@ -50,7 +50,7 @@ struct ExampleProps {
     name: String,
 }
 
-static Example2: FC<ExampleProps> = |cx| {
+pub static Example2: FC<ExampleProps> = |cx| {
     cx.render(rsx! {
         div {
             h1 {"hello {cx.name}"}

+ 1 - 1
examples/_examples/input.rs

@@ -37,7 +37,7 @@ static App: FC<()> = |cx| {
     })
 };
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div { class: "max-w-lg max-w-xs bg-blue-800 shadow-2xl rounded-lg mx-auto text-center py-12 mt-4 rounded-xl"
             div { class: "container py-5 max-w-md mx-auto"

+ 1 - 1
examples/_examples/jackjill.rs

@@ -9,7 +9,7 @@ fn main() {
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
 }
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let (name, set_name) = use_state_classic(cx, || "...?");
 
     log::debug!("Running component....");

+ 1 - 1
examples/_examples/jackjillrsx.rs

@@ -9,7 +9,7 @@ fn main() {
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
 }
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let (name, set_name) = use_state_classic(cx, || "...?");
     cx.render(rsx! {
         section { class: "py-12 px-4 text-center"

+ 1 - 1
examples/_examples/many.rs

@@ -11,7 +11,7 @@ fn main() {
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
 }
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
             span {

+ 1 - 1
examples/_examples/rsxt.rs

@@ -27,7 +27,7 @@ struct ExampleProps {
     initial_name: &'static str,
 }
 
-static Example: FC<ExampleProps> = |cx| {
+pub static Example: FC<ExampleProps> = |cx| {
     let name = use_state(cx, move || cx.initial_name);
 
     cx.render(rsx! {

+ 1 - 1
examples/doggo.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let (g, set_g) = use_state(cx, || 0).classic();
     let v = (0..10).map(move |f| {
         rsx!(li {

+ 2 - 2
examples/examples/demo.rs

@@ -17,7 +17,7 @@ fn main() {
     .expect("Webview finished");
 }
 
-// static Example: FC<()> = |cx| {
+// pub static Example: FC<()> = |cx| {
 //     cx.render(html! {
 //         <div>
 //         <svg class="octicon octicon-star v-align-text-bottom"
@@ -36,7 +36,7 @@ fn main() {
 //         </div>
 //     })
 // };
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div  {
             class: "flex items-center justify-center flex-col"

+ 2 - 6
examples/model.rs

@@ -20,12 +20,8 @@ use dioxus::prelude::*;
 
 const STYLE: &str = include_str!("./assets/calculator.css");
 fn main() {
-    dioxus::desktop::launch(App, |cfg| {
-        cfg.with_title("Calculator Demo")
-            .with_resizable(true)
-            .with_skip_taskbar(true)
-    })
-    .expect("failed to launch dioxus app");
+    dioxus::desktop::launch(App, |cfg| cfg.with_title("Calculator Demo"))
+        .expect("failed to launch dioxus app");
 }
 
 static App: FC<()> = |cx| {

+ 7 - 2
examples/reference/antipatterns.rs

@@ -16,8 +16,6 @@
 //! Feel free to file a PR or Issue if you run into another antipattern that you think users of Dioxus should know about.
 use dioxus::prelude::*;
 
-fn main() {}
-
 /// Antipattern: Iterators without keys
 /// -----------------------------------
 ///
@@ -174,3 +172,10 @@ static _example: FC<()> = |cx| todo!();
 /// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific
 /// libraries.
 static __example: FC<()> = |cx| todo!();
+
+pub static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        AntipatternNoKeys { data: std::collections::HashMap::new() }
+        AntipatternNestedFragments {}
+    })
+};

+ 0 - 10
examples/reference/async.rs

@@ -1,10 +0,0 @@
-use dioxus::prelude::*;
-fn main() {}
-
-static Example: FC<()> = |cx| {
-    cx.render(rsx! {
-        div {
-
-        }
-    })
-};

+ 29 - 2
examples/reference/basics.rs

@@ -1,10 +1,37 @@
+//! Example: The basics of Dioxus
+//! ----------------------------
+//!
+//! This small example covers some of the basics of Dioxus including
+//! - Components
+//! - Props
+//! - Children
+//! - the rsx! macro
+
 use dioxus::prelude::*;
-fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
+            Greeting {
+                name: "Dioxus"
+                div { "Dioxus is a fun, fast, and portable UI framework for Rust" }
+            }
+        }
+    })
+};
 
+#[derive(PartialEq, Props)]
+struct GreetingProps {
+    name: &'static str,
+}
+
+static Greeting: FC<GreetingProps> = |cx| {
+    cx.render(rsx! {
+        div {
+            h1 { "Hello, {cx.name}!" }
+            p { "Welcome to the Diouxs framework" }
+            br {}
+            {cx.children()}
         }
     })
 };

+ 2 - 3
examples/reference/children.rs

@@ -17,9 +17,8 @@
 //! In the future, this might become a runtime error, so consider it an error today.
 
 use dioxus::prelude::*;
-fn main() {}
 
-static App: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
             Banner {
@@ -32,7 +31,7 @@ static App: FC<()> = |cx| {
     })
 };
 
-static Banner: FC<()> = |cx| {
+pub static Banner: FC<()> = |cx| {
     cx.render(rsx! {
         div {
             h1 { "This is a great banner!" }

+ 33 - 9
examples/reference/conditional_rendering.rs

@@ -11,14 +11,12 @@
 
 use dioxus::prelude::*;
 
-fn main() {}
-
 // Convert a boolean conditional into a hide/show
 #[derive(PartialEq, Props)]
-struct MyProps {
+pub struct MyProps {
     should_show: bool,
 }
-static Example: FC<MyProps> = |cx| {
+pub static Example0: FC<MyProps> = |cx| {
     cx.render(rsx! {
         div {
             {cx.should_show.then(|| rsx!{
@@ -38,10 +36,10 @@ static Example: FC<MyProps> = |cx| {
 // In short:
 // `rsx!(in cx, ...)` is shorthand for `cx.render(rsx!(...))`
 #[derive(PartialEq, Props)]
-struct MyProps1 {
+pub struct MyProps1 {
     should_show: bool,
 }
-static Example1: FC<MyProps1> = |cx| {
+pub static Example1: FC<MyProps1> = |cx| {
     cx.render(rsx! {
         div {
             // With matching
@@ -70,16 +68,16 @@ static Example1: FC<MyProps1> = |cx| {
 /// Matching can be expanded
 
 #[derive(PartialEq)]
-enum Color {
+pub enum Color {
     Green,
     Yellow,
     Red,
 }
 #[derive(PartialEq, Props)]
-struct MyProps2 {
+pub struct MyProps2 {
     color: Color,
 }
-static Example2: FC<MyProps2> = |cx| {
+pub static Example2: FC<MyProps2> = |cx| {
     cx.render(rsx! {
         div {
             {match cx.color {
@@ -90,3 +88,29 @@ static Example2: FC<MyProps2> = |cx| {
         }
     })
 };
+
+pub static Example: FC<()> = |cx| {
+    let should_show = use_state(cx, || false);
+    let mut color_index = use_state(cx, || 0);
+    let color = match *color_index % 2 {
+        2 => Color::Green,
+        1 => Color::Yellow,
+        _ => Color::Red,
+    };
+
+    cx.render(rsx! {
+        div {
+            button {
+                onclick: move |_| should_show.set(!*should_show)
+                "click to toggle showing the examples"
+            }
+            button {
+                onclick: move |_| color_index += 1
+                "click to for the enxt color"
+            }
+            Example0 { should_show: *should_show }
+            Example1 { should_show: *should_show }
+            Example2 { color: color }
+        }
+    })
+};

+ 28 - 1
examples/reference/controlled_inputs.rs

@@ -1,10 +1,37 @@
 use dioxus::prelude::*;
 fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
 
         }
     })
 };
+
+// A controlled component:
+static ControlledSelect: FC<()> = |cx| {
+    let value = use_state(cx, || String::from("Grapefruit"));
+    cx.render(rsx! {
+        select { value: "{value}", onchange: move |evt| value.set(evt.value()),
+            option { value: "Grapefruit", "Grapefruit"}
+            option { value: "Lime", "Lime"}
+            option { value: "Coconut", "Coconut"}
+            option { value: "Mango", "Mango"}
+        }
+    })
+};
+
+// TODO - how do uncontrolled things work?
+static UncontrolledSelect: FC<()> = |cx| {
+    let value = use_state(cx, || String::new());
+
+    cx.render(rsx! {
+        select {
+            option { value: "Grapefruit", "Grapefruit"}
+            option { value: "Lime", "Lime"}
+            option { value: "Coconut", "Coconut"}
+            option { value: "Mango", "Mango"}
+        }
+    })
+};

+ 37 - 3
examples/reference/custom_elements.rs

@@ -1,10 +1,44 @@
+//! Example: Web Components & Custom Elements
+//! -----------------------------------------
+//!
+//! Web components are a flavor of html elements that can be user-defined.
+//! See: https://www.webcomponents.org for more information.
+//!
+//! Users who use webcomponents typically don't use Dioxus. However, if you would like to use webcomponents in Dioxus,
+//! you can easily create a new custom element with compile-time correct wrappers around the webcomponent.
+//!
+//! We don't support building new webcomponents with Dioxus, however.  :(
+
 use dioxus::prelude::*;
-fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
-
+            custom_element {
+                custom_attr: "custom data on custom elements"
+            }
         }
     })
 };
+
+mod dioxus_elements {
+    use std::fmt::Arguments;
+
+    use dioxus::prelude::DioxusElement;
+
+    #[allow(non_camel_case_types)]
+    pub struct custom_element;
+    impl DioxusElement for custom_element {
+        const TAG_NAME: &'static str = "custom_element";
+        const NAME_SPACE: Option<&'static str> = None;
+    }
+    impl custom_element {
+        pub fn custom_attr<'a>(&self, f: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+            f.attr("custom_asttr", val, None, false)
+        }
+    }
+
+    // Re-export the normal html namespace
+    pub use dioxus::prelude::dioxus_elements::*;
+    use dioxus_core::{nodes::Attribute, NodeFactory};
+}

+ 1 - 3
examples/reference/empty.rs

@@ -3,8 +3,6 @@
 //!
 //! This is a simple pattern that allows you to return no elements!
 
-fn main() {}
-
 use dioxus::prelude::*;
 
-static Example: FC<()> = |cx| cx.render(rsx! { Fragment {} });
+pub static Example: FC<()> = |cx| cx.render(rsx! { Fragment {} });

+ 11 - 6
examples/reference/fragments.rs

@@ -9,21 +9,18 @@
 //! - By using the fragment() method on the node factory
 
 use dioxus::prelude::*;
-fn main() {}
 
 // Returning multiple elements with rsx! or html!
-static Example1: FC<()> = |cx| {
+static App1: FC<()> = |cx| {
     cx.render(rsx! {
         h1 { }
         h2 { }
         h3 { }
-        // {}
-        // "hello world"
     })
 };
 
 // Using the Fragment component
-static Example2: FC<()> = |cx| {
+static App2: FC<()> = |cx| {
     cx.render(rsx! {
         Fragment {
             div {}
@@ -34,7 +31,7 @@ static Example2: FC<()> = |cx| {
 };
 
 // Using the `fragment` method on the NodeFactory
-static Example3: FC<()> = |cx| {
+static App3: FC<()> = |cx| {
     cx.render(LazyNodes::new(move |fac| {
         fac.fragment_from_iter([
             fac.text(format_args!("A")),
@@ -44,3 +41,11 @@ static Example3: FC<()> = |cx| {
         ])
     }))
 };
+
+pub static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        App1 {}
+        App2 {}
+        App3 {}
+    })
+};

+ 1 - 2
examples/reference/global_css.rs

@@ -12,7 +12,6 @@
 //! A coming update with the assets system will make it possible to include global css from child components.
 
 use dioxus::prelude::*;
-fn main() {}
 
 const STYLE: &str = r#"
 body {background-color: powderblue;}
@@ -20,7 +19,7 @@ h1   {color: blue;}
 p    {color: red;}
 "#;
 
-const Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         head { style { "{STYLE}" } }
         body {

+ 31 - 3
examples/reference/inline_styles.rs

@@ -1,10 +1,38 @@
+//! Example: Inline Styles
+//! ----------------------
+//!
+//! This example shows how to use inline styles in Dioxus components.
+//!
+//! Inline styles function very similar to regular attributes, just grouped together in "style".
+//!
+//! Inline styles in Dioxus are more performant than React since we're able to cache attributes and compare by pointers.
+//! However, it's still not as performant as cascaded styles. Use with care.
+
 use dioxus::prelude::*;
-fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
-        div {
+        head {
+            style: { background_color: "powderblue" }
+         }
+        body {
+            h1 { style: { color: "blue" }
+                "This is a heading"
+            }
+            p { style: { color: "red" }
+                "This is a paragraph"
+            }
+        }
+    })
+};
 
+// .... technically the rsx! macro is slightly broken at the moment and alows styles not wrapped in style {}
+// I haven't noticed any name collisions yet, and am tentatively leaving this behavior in..
+// Don't rely on it.
+static Example2: FC<()> = |cx| {
+    cx.render(rsx! {
+        div { color: "red"
+            "hello world!"
         }
     })
 };

+ 7 - 7
examples/reference/iterators.rs

@@ -7,17 +7,17 @@
 //! resolves to VNodes. It's more efficient and easier to write than having to `collect` everywhere.
 //!
 //! This also makes it easy to write "pull"-style iterators that don't have a known size.
-
-fn main() {}
+//!
+//! However, when the size of an iterator needs to be know for display purposes, collecting is fine.
 
 use dioxus::prelude::*;
-static Example: FC<()> = |cx| {
-    let g = use_state(cx, || 0);
+
+pub static Example: FC<()> = |cx| {
+    let example_data = use_state(cx, || 0);
 
     let v = (0..10).map(|f| {
         rsx! {
-            li {
-                onclick: move |_| g.set(f)
+            li { onclick: move |_| example_data.set(f)
                 "ID: {f}"
                 ul {
                     {(0..10).map(|k| rsx!{
@@ -31,7 +31,7 @@ static Example: FC<()> = |cx| {
     });
 
     cx.render(rsx! {
-        h3 {"Selected: {g}"}
+        h3 {"Selected: {example_data}"}
         ul {
             {v}
         }

+ 7 - 1
examples/reference/listener.rs

@@ -7,7 +7,13 @@
 
 use dioxus::prelude::*;
 
-fn main() {}
+pub static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        ButtonList {}
+        NonUpdatingEvents {}
+        DisablePropogation {}
+    })
+};
 
 /// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
 static ButtonList: FC<()> = |cx| {

+ 165 - 2
examples/reference/main.rs

@@ -1,7 +1,170 @@
-// mod iterator;
-// mod memo;
+#![allow(
+    unused,
+    dead_code,
+    non_upper_case_globals,
+    non_camel_case_types,
+    non_snake_case
+)]
+
+mod antipatterns;
+mod basics;
+mod children;
+mod conditional_rendering;
+mod controlled_inputs;
+mod custom_elements;
+mod empty;
+mod fragments;
+mod global_css;
+mod inline_styles;
 mod iterators;
 mod listener;
 mod memo;
+mod noderefs;
+mod signals;
+mod spreadpattern;
+mod statemanagement;
+mod suspense;
+mod task;
+mod testing;
+mod tostring;
 
 fn main() {}
+
+use std::rc::Rc;
+
+use dioxus::prelude::*;
+
+static App: FC<()> = |cx| {
+    let (selection, set_selection) = use_state(cx, || None as Option<usize>).classic();
+
+    let body = match selection {
+        Some(id) => rsx!(in cx, ReferenceItem { selected: *id }),
+        None => rsx!(in cx, div { "Select an concept to explore" }),
+    };
+
+    cx.render(rsx! {
+        div {
+            ScrollSelector { onselect: move |id| set_selection(id)  }
+            {body}
+        }
+    })
+};
+
+// this is its own component to stay memoized
+#[derive(Props)]
+struct ScrollSelectorProps<'a> {
+    onselect: &'a dyn Fn(Option<usize>),
+}
+
+fn ScrollSelector<'a>(cx: Context<'a, ScrollSelectorProps>) -> VNode<'a> {
+    let selection_list = (&REFERENCES).iter().enumerate().map(|(id, _)| {
+        rsx! {
+            li {
+                h3 {}
+            }
+        }
+    });
+    cx.render(rsx! {
+        div {
+            h1 {""}
+            ul {
+                {selection_list}
+                button {
+                    onclick: move |_| (cx.onselect)(Some(10))
+                }
+            }
+        }
+    })
+}
+
+#[derive(PartialEq, Props)]
+struct ReferenceItemProps {
+    selected: usize,
+}
+
+static ReferenceItem: FC<ReferenceItemProps> = |cx| {
+    let (caller, name, code) = REFERENCES[cx.selected];
+
+    // Create the component using the factory API directly
+    let caller_node = LazyNodes::new(move |f| f.component(caller, (), None, &[]));
+
+    cx.render(rsx! {
+        div {
+            // Source of the left, rendered on the right
+            div {
+                code { "{code}" }
+            }
+            div {
+                {caller_node}
+            }
+        }
+    })
+};
+
+static REFERENCES: &[(FC<()>, &str, &str)] = &[
+    (basics::Example, "Basics", include_str!("./basics.rs")),
+    (children::Example, "Children", include_str!("./children.rs")),
+    (
+        conditional_rendering::Example,
+        "Conditional Rendering",
+        include_str!("./conditional_rendering.rs"),
+    ),
+    // TODO
+    (
+        controlled_inputs::Example,
+        "Controlled Inputs",
+        include_str!("./controlled_inputs.rs"),
+    ),
+    (empty::Example, "empty", include_str!("./empty.rs")),
+    (
+        custom_elements::Example,
+        "Custom Elements & Web Components",
+        include_str!("./custom_elements.rs"),
+    ),
+    (
+        fragments::Example,
+        "Fragments",
+        include_str!("./fragments.rs"),
+    ),
+    (
+        iterators::Example,
+        "Iterators",
+        include_str!("./iterators.rs"),
+    ),
+    (
+        global_css::Example,
+        "Global CSS",
+        include_str!("./global_css.rs"),
+    ),
+    (
+        inline_styles::Example,
+        "Inline Styles",
+        include_str!("./inline_styles.rs"),
+    ),
+    (listener::Example, "Listener", include_str!("./listener.rs")),
+    (memo::Example, "Memo", include_str!("./memo.rs")),
+    (
+        spreadpattern::Example,
+        "Spread Pattern",
+        include_str!("./spreadpattern.rs"),
+    ),
+    (suspense::Example, "Suspense", include_str!("./suspense.rs")),
+    (task::Example, "Task", include_str!("./task.rs")),
+    (tostring::Example, "Tostring", include_str!("./tostring.rs")),
+    (
+        antipatterns::Example,
+        "Anti-patterns",
+        include_str!("./antipatterns.rs"),
+    ),
+    /*
+        TODO!
+    */
+    (signals::Example, "Signals", include_str!("./signals.rs")),
+    (noderefs::Example, "NodeRefs", include_str!("./noderefs.rs")),
+    (
+        statemanagement::Example,
+        "State Management",
+        include_str!("./statemanagement.rs"),
+    ),
+    (testing::Example, "Testing", include_str!("./testing.rs")),
+];

+ 8 - 9
examples/reference/memo.rs

@@ -18,11 +18,10 @@
 //! memoized collections like im-rc which are designed for this use case.
 
 use dioxus::prelude::*;
-fn main() {}
 
 // By default, components with no props are always memoized.
 // A props of () is considered empty.
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div { "100% memoized!" }
     })
@@ -32,11 +31,11 @@ static Example: FC<()> = |cx| {
 // However, the parent *must* create a new string on every render.
 // Notice how these props implement PartialEq - this is required for 'static props
 #[derive(PartialEq, Props)]
-struct MyProps1 {
+pub struct MyProps1 {
     name: String,
 }
 
-static Example1: FC<MyProps1> = |cx| {
+pub static Example1: FC<MyProps1> = |cx| {
     cx.render(rsx! {
         div { "100% memoized! {cx.name}" }
     })
@@ -46,11 +45,11 @@ static Example1: FC<MyProps1> = |cx| {
 // In contrast with the `String` example, these props use `Rc<str>` which operates similar to strings in JavaScript.
 // These strings cannot be modified, but may be cheaply shared in many places without issue.
 #[derive(PartialEq, Props)]
-struct MyProps2 {
+pub struct MyProps2 {
     name: std::rc::Rc<str>,
 }
 
-static Example2: FC<MyProps2> = |cx| {
+pub static Example2: FC<MyProps2> = |cx| {
     cx.render(rsx! {
         div { "100% memoized! {cx.name}" }
     })
@@ -58,11 +57,11 @@ static Example2: FC<MyProps2> = |cx| {
 
 // These props *do* borrow any content, and therefore cannot be safely memoized!.
 #[derive(PartialEq, Props)]
-struct MyProps3<'a> {
+pub struct MyProps3<'a> {
     name: &'a str,
 }
 // We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
-// Using the `static Example: FC<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might
+// Using the `pub static Example: FC<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might
 // not exactly be what you want
 fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode {
     cx.render(rsx! {
@@ -73,7 +72,7 @@ fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode {
 // These props *do* borrow any content, and therefore cannot be safely memoized!.
 // However, they cannot be compared, so we don't need the PartialEq flag.
 #[derive(Props)]
-struct MyProps4<'a> {
+pub struct MyProps4<'a> {
     onhandle: &'a dyn Fn(),
 }
 

+ 1 - 1
examples/reference/noderefs.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
 

+ 1 - 1
examples/reference/signals.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
 

+ 4 - 5
examples/reference/spreadpattern.rs

@@ -8,27 +8,26 @@
 //! values, using the manual props as the base and then modifying fields specified with non-spread attributes.
 
 use dioxus::prelude::*;
-fn main() {}
 
-static App: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let props = MyProps {
         count: 0,
         live: true,
         name: "Dioxus",
     };
     cx.render(rsx! {
-        Example { ..props, count: 10, div {"child"} }
+        Example1 { ..props, count: 10, div {"child"} }
     })
 };
 
 #[derive(PartialEq, Props)]
-struct MyProps {
+pub struct MyProps {
     count: u32,
     live: bool,
     name: &'static str,
 }
 
-static Example: FC<MyProps> = |cx| {
+pub static Example1: FC<MyProps> = |cx| {
     cx.render(rsx! {
         div {
             h1 { "Hello, {cx.name}"}

+ 1 - 1
examples/reference/statemanagement.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
 

+ 11 - 42
examples/reference/suspense.rs

@@ -1,27 +1,27 @@
 //! Example: Suspense
 //! -----------------
-//! This example shows how the "use_fetch" hook is built on top of Dioxus' "suspense" API. Suspense enables components
-//! to wait on futures to complete before rendering the result into VNodes. These VNodes are immediately available in a
-//! "suspended" fashion and will automatically propogate to the UI when the future completes.
+//! This example demonstrates how the use_suspense hook can be used to load and render asynchronous data.  Suspense enables
+//! components to wait on futures to complete before rendering the result into VNodes. These VNodes are immediately
+//! available in a suspended" fashion and will automatically propogate to the UI when the future completes.
 //!
-//! Note that if your component updates or receives new props while it is awating the result, the future will be dropped
-//! and all work will stop. In this example, we store the future in a hook so we can always resume it.
+//! Currently, suspense futures are non-restartable. In the future, we'll provide more granular control of how to start,
+//! stop, and reset these futures.
 
 use dioxus::prelude::*;
-fn main() {}
 #[derive(serde::Deserialize)]
 struct DogApi {
     message: String,
 }
 const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
 
-pub static App: FC<()> = |cx| {
-    let doggo = use_future_effect(cx, move || async move {
-        match surf::get(ENDPOINT).recv_json::<DogApi>().await {
+pub static Example: FC<()> = |cx| {
+    let doggo = cx.use_suspense(
+        || surf::get(ENDPOINT).recv_json::<DogApi>(),
+        |cx, res| match res {
             Ok(res) => rsx!(in cx, img { src: "{res.message}" }),
             Err(_) => rsx!(in cx, div { "No doggos for you :(" }),
-        }
-    });
+        },
+    );
 
     cx.render(rsx!(
         div {
@@ -30,34 +30,3 @@ pub static App: FC<()> = |cx| {
         }
     ))
 };
-
-// use dioxus_core::virtual_dom::SuspendedContext;
-use futures::Future;
-use futures::FutureExt;
-use std::pin::Pin;
-
-fn use_fetch<'a, T, P, F>(cx: Context<P>, url: &str, g: F) -> VNode<'a>
-where
-    T: serde::de::DeserializeOwned + 'static,
-    // F: FnOnce(SuspendedContext, surf::Result<T>) -> VNode<'a> + 'a,
-    F: FnOnce((), surf::Result<T>) -> VNode<'a> + 'a,
-{
-    todo!()
-    // // essentially we're doing a "use_effect" but with no dependent props or post-render shenanigans
-    // let fetch_promise = cx.use_hook(
-    //     move || surf::get(url).recv_json::<T>().boxed_local(),
-    //     // just pass the future through
-    //     |p| p,
-    //     |_| (),
-    // );
-    // cx.suspend(fetch_promise, g)
-}
-
-/// Spawns the future only when the inputs change
-fn use_future_effect<'a, 'b, F, P, F2>(cx: Context<P>, g: F2) -> VNode<'a>
-where
-    F: Future<Output = VNode<'b>>,
-    F2: FnOnce() -> F + 'a,
-{
-    todo!()
-}

+ 61 - 0
examples/reference/task.rs

@@ -0,0 +1,61 @@
+//! Example: Tasks
+//! --------------
+//!
+//! Built around the same system that powers suspense, Dioxus also allows users to write arbitrary tasks that can modify
+//! state asynchronously. `use_task` takes a future and returns a task handle and an option that holds the tasks's return
+//! value. When the task completes, the component will re-render with the result freshly available.
+//!
+//! Tasks don't necessarily need to complete, however. It would be completely reasonable to wire up a websocket receiver
+//! in a task and have it work infinitely while the app is running. If the socket throws an error, the task could complete
+//! and the UI could present a helpful error message.
+//!
+//! Tasks also return the `TaskHandle` type which lets other component logic start, stop, and restart the task at any time.
+//! Tasks are very much like an async-flavoroued coroutine, making them incredibly powerful.
+//!
+//! Tasks must be valid for the 'static lifetime, so any state management neeeds to be cloned into the closure. `use_state`
+//! has a method called `for_async` which generates an AsyncUseState wrapper. This has a very similar API to the regualr
+//! `use_state` but is `static.
+//!
+//! Remeber `use_task` is a hook! Don't call it out of order or in loops. You might aaccidentally swap the task handles
+//! and break things in subtle ways.
+//!
+//! Whenever a component is scheduled for deletion, the task is dropped. Make sure that whatever primitives used in the
+//! task have a valid drop implementation and won't leave resources tied up.
+
+use dioxus::prelude::*;
+
+pub static Example: FC<()> = |cx| {
+    let count = use_state(cx, || 0);
+    let mut direction = use_state(cx, || 1);
+
+    // Tasks are 'static, so we need to copy relevant items in
+    let (async_count, dir) = (count.for_async(), *direction);
+    let (task, result) = cx.use_task(move || async move {
+        // Count infinitely!
+        loop {
+            gloo_timers::future::TimeoutFuture::new(250).await;
+            *async_count.get_mut() += dir;
+        }
+    });
+
+    cx.render(rsx! {
+        div {
+            h1 {"count is {count}"}
+            button {
+                "Stop counting"
+                onclick: move |_| task.stop()
+            }
+            button {
+                "Start counting"
+                onclick: move |_| task.start()
+            }
+            button {
+                "Switch counting direcion"
+                onclick: move |_| {
+                    direction *= -1;
+                    task.restart();
+                }
+            }
+        }
+    })
+};

+ 1 - 2
examples/reference/testing.rs

@@ -1,7 +1,6 @@
 use dioxus::prelude::*;
-fn main() {}
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     cx.render(rsx! {
         div {
 

+ 18 - 3
examples/reference/tostring.rs

@@ -1,10 +1,25 @@
 use dioxus::prelude::*;
-fn main() {}
+use dioxus::ssr;
+
+pub static Example: FC<()> = |cx| {
+    let as_string = use_state(cx, || {
+        // Currently, SSR is only supported for whole VirtualDOMs
+        // This is an easy/low hanging fruit to improve upon
+        let mut dom = VirtualDom::new(SomeApp);
+        dom.rebuild_in_place().unwrap();
+        ssr::render_root(&dom)
+    });
 
-static Example: FC<()> = |cx| {
     cx.render(rsx! {
-        div {
+        div { "{as_string}" }
+    })
+};
 
+static SomeApp: FC<()> = |cx| {
+    cx.render(rsx! {
+        div { style: {background_color: "blue"}
+            h1 {"Some amazing app or component"}
+            p {"Things are great"}
         }
     })
 };

+ 0 - 26
examples/reference/webcomponents.rs

@@ -1,26 +0,0 @@
-//! Example: Web Components
-//! -----------------------
-//!
-//! Web components are a flavor of html elements that can be user-defined.
-//! See: https://www.webcomponents.org for more information.
-//!
-//! Users who use webcomponents typically don't use Dioxus. However, if you would like to use webcomponents in Dioxus,
-//! you can easily create a new custom element with compile-time correct wrappers around the webcomponent.
-//!
-//! We don't support building new webcomponents with Dioxus, however.
-//!
-
-fn main() {}
-
-mod dioxus_elements {
-    use dioxus::prelude::DioxusElement;
-
-    struct custom_element;
-    impl DioxusElement for custom_element {
-        const TAG_NAME: &'static str = "custom_element";
-        const NAME_SPACE: Option<&'static str> = None;
-    }
-
-    // Re-export the normal html namespace
-    pub use dioxus::prelude::dioxus_elements::*;
-}

+ 1 - 1
examples/rsx_usage.rs

@@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
 use baller::Baller;
 use dioxus::prelude::*;
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let formatting = "formatting!";
     let formatting_tuple = ("a", "b");
     let lazy_fmt = format_args!("lazily formatted text");

+ 6 - 6
examples/tailwind.rs

@@ -12,7 +12,7 @@ fn main() {
 
 const STYLE: &str = "body {overflow:hidden;}";
 
-pub const App: FC<()> = |cx| {
+pub static App: FC<()> = |cx| {
     cx.render(rsx!(
         div { class: "overflow-hidden"
         style { "{STYLE}" }
@@ -28,7 +28,7 @@ pub const App: FC<()> = |cx| {
     ))
 };
 
-pub const Header: FC<()> = |cx| {
+pub static Header: FC<()> = |cx| {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font"
@@ -54,7 +54,7 @@ pub const Header: FC<()> = |cx| {
     })
 };
 
-pub const Hero: FC<()> = |cx| {
+pub static Hero: FC<()> = |cx| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -92,7 +92,7 @@ pub const Hero: FC<()> = |cx| {
         }
     })
 };
-pub const Entry: FC<()> = |cx| {
+pub static Entry: FC<()> = |cx| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -105,7 +105,7 @@ pub const Entry: FC<()> = |cx| {
     })
 };
 
-pub const StacksIcon: FC<()> = |cx| {
+pub static StacksIcon: FC<()> = |cx| {
     cx.render(rsx!(
         svg {
             // xmlns: "http://www.w3.org/2000/svg"
@@ -120,7 +120,7 @@ pub const StacksIcon: FC<()> = |cx| {
         }
     ))
 };
-pub const RightArrowIcon: FC<()> = |cx| {
+pub static RightArrowIcon: FC<()> = |cx| {
     cx.render(rsx!(
         svg {
             fill: "none"

+ 114 - 0
examples/todomvc.rs

@@ -0,0 +1,114 @@
+use dioxus::prelude::*;
+use im_rc::HashMap;
+use std::rc::Rc;
+
+fn main() {
+    #[cfg(feature = "desktop")]
+    // #[cfg(not(target_arch = "wasm32"))]
+    dioxus::desktop::launch(App, |c| c);
+
+    #[cfg(feature = "desktop")]
+    dioxus::web::launch(App, |c| c);
+}
+
+#[derive(PartialEq)]
+pub enum FilterState {
+    All,
+    Active,
+    Completed,
+}
+
+#[derive(Debug, PartialEq)]
+pub struct TodoItem {
+    pub id: u32,
+    pub checked: bool,
+    pub contents: String,
+}
+
+const STYLE: &str = include_str!("./_examples/todomvc/style.css");
+const App: FC<()> = |cx| {
+    let draft = use_state(cx, || "".to_string());
+    let todos = use_state(cx, || HashMap::<u32, Rc<TodoItem>>::new());
+    let filter = use_state(cx, || FilterState::All);
+
+    let todolist = todos
+        .iter()
+        .filter(|(id, item)| match *filter {
+            FilterState::All => true,
+            FilterState::Active => !item.checked,
+            FilterState::Completed => item.checked,
+        })
+        .map(|(id, todo)| {
+            rsx!(TodoEntry {
+                key: "{id}",
+                todo: todo.clone()
+            })
+        })
+        .collect::<Vec<_>>();
+
+    let items_left = todolist.len();
+    let item_text = match items_left {
+        1 => "item",
+        _ => "items",
+    };
+
+    cx.render(rsx! {
+        div { id: "app"
+            style {"{STYLE}"}
+            div {
+                header { class: "header"
+                    h1 {"todos"}
+                    input {
+                        class: "new-todo"
+                        placeholder: "What needs to be done?"
+                        value: "{draft}"
+                        oninput: move |evt| draft.set(evt.value())
+                    }
+                }
+                {todolist}
+                {(!todos.is_empty()).then(|| rsx!(
+                    footer {
+                        span { strong {"{items_left}"} span {"{item_text} left"} }
+                        ul { class: "filters"
+                            li { class: "All", a { href: "", onclick: move |_| filter.set(FilterState::All), "All" }}
+                            li { class: "Active", a { href: "active", onclick: move |_| filter.set(FilterState::Active), "Active" }}
+                            li { class: "Completed", a { href: "completed", onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
+                        }
+                    }
+                ))}
+            }
+            footer { class: "info"
+                p {"Double-click to edit a todo"}
+                p { "Created by ", a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }}
+                p { "Part of ", a { "TodoMVC", href: "http://todomvc.com" }}
+            }
+        }
+    })
+};
+
+#[derive(PartialEq, Props)]
+pub struct TodoEntryProps {
+    todo: std::rc::Rc<TodoItem>,
+}
+
+pub fn TodoEntry(cx: Context<TodoEntryProps>) -> VNode {
+    let TodoEntryProps { todo } = *cx;
+    let is_editing = use_state(cx, || false);
+    let contents = "";
+
+    cx.render(rsx! (
+        li {
+            "{todo.id}"
+            input {
+                class: "toggle"
+                r#type: "checkbox"
+                "{todo.checked}"
+            }
+           {is_editing.then(|| rsx!{
+                input {
+                    value: "{contents}"
+                }
+            })}
+        }
+    ))
+}

+ 16 - 16
notes/Parity.md

@@ -12,7 +12,7 @@ https://github.com/rustwasm/gloo
 For example, resize observer would function like this:
 
 ```rust
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     let observer = use_resize_observer();
 
     cx.render(rsx!(
@@ -29,18 +29,18 @@ For other services, we shell out to gloo. If the gloo service doesn't exist, the
 
 | Service                      | Hook examples | Current Projects |
 | ---------------------------- | ------------- | ---------------- |
-| Fetch                        | 👀            | Reqwest/surf     |
-| Local storage (cache)        | 👀            | Gloo             |
-| Persistent storage (IndexDB) | 👀            | 👀               |
-| WebSocket                    | 👀            | Gloo             |
-| 3D Renderer / WebGL          | 👀            | Gloo             |
-| Web Worker                   | 👀            | 👀               |
-| Router                       | 👀            | 👀               |
-| Notifications                | 👀            | 👀               |
-| WebRTC Client                | 👀            | 👀               |
-| Service Workers              | 👀            | 👀               |
-| Resize Observer              | 👀            | 👀               |
-| Canvas                       | 👀            | 👀               |
-| Clipboard                    | 👀            | 👀               |
-| Fullscreen                   | 👀            | 👀               |
-| History API                  | 👀            | 👀               |
+| Fetch                        | 👀             | Reqwest/surf     |
+| Local storage (cache)        | 👀             | Gloo             |
+| Persistent storage (IndexDB) | 👀             | 👀                |
+| WebSocket                    | 👀             | Gloo             |
+| 3D Renderer / WebGL          | 👀             | Gloo             |
+| Web Worker                   | 👀             | 👀                |
+| Router                       | 👀             | 👀                |
+| Notifications                | 👀             | 👀                |
+| WebRTC Client                | 👀             | 👀                |
+| Service Workers              | 👀             | 👀                |
+| Resize Observer              | 👀             | 👀                |
+| Canvas                       | 👀             | 👀                |
+| Clipboard                    | 👀             | 👀                |
+| Fullscreen                   | 👀             | 👀                |
+| History API                  | 👀             | 👀                |

+ 2 - 2
notes/SOLVEDPROBLEMS.md

@@ -153,13 +153,13 @@ Notice that LiveComponent receivers (the client-side interpretation of a LiveCom
 The `VNodeTree` type is a very special type that allows VNodes to be created using a pluggable allocator. The html! macro creates something that looks like:
 
 ```rust
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     html! { <div> "blah" </div> }
 };
 
 // expands to...
 
-static Example: FC<()> = |cx| {
+pub static Example: FC<()> = |cx| {
     // This function converts a Fn(allocator) -> VNode closure to a VNode struct that will later be evaluated.
     html_macro_to_vnodetree(move |allocator| {
         let mut node0 = allocator.alloc(VElement::div);

+ 8 - 22
packages/core-macro/src/rsx/ambiguous.rs

@@ -39,32 +39,18 @@ impl Parse for AmbiguousElement {
             }
         }
 
-        // if input.peek(Ident) {
-        //     let name_str = input.fork().parse::<Ident>().unwrap().to_string();
-        // } else {
-        // }
-
         if let Ok(name) = input.fork().parse::<Ident>() {
             let name_str = name.to_string();
 
-            match is_valid_tag(&name_str) {
-                true => input
+            let first_char = name_str.chars().next().unwrap();
+            if first_char.is_ascii_uppercase() {
+                input
+                    .parse::<Component>()
+                    .map(|c| AmbiguousElement::Component(c))
+            } else {
+                input
                     .parse::<Element>()
-                    .map(|c| AmbiguousElement::Element(c)),
-                false => {
-                    let first_char = name_str.chars().next().unwrap();
-                    if first_char.is_ascii_uppercase() {
-                        input
-                            .parse::<Component>()
-                            .map(|c| AmbiguousElement::Component(c))
-                    } else {
-                        let name = input.parse::<Ident>().unwrap();
-                        Err(Error::new(
-                            name.span(),
-                            "Components must be uppercased, perhaps you mispelled a html tag",
-                        ))
-                    }
-                }
+                    .map(|c| AmbiguousElement::Element(c))
             }
         } else {
             if input.peek(LitStr) {

+ 2 - 2
packages/core-macro/src/rsx/component.rs

@@ -96,7 +96,7 @@ pub fn parse_component_body(
             }
             content.parse::<Token![..]>()?;
             *manual_props = Some(content.parse::<Expr>()?);
-        } else if content.peek(Ident) && content.peek2(Token![:]) {
+        } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
             if !cfg.allow_fields {
                 return Err(Error::new(
                     content.span(),
@@ -184,7 +184,7 @@ impl ToTokens for Component {
         };
 
         tokens.append_all(quote! {
-            __cx.virtual_child(
+            __cx.component(
                 #name,
                 #builder,
                 #key_token,

+ 0 - 5
packages/core-macro/src/rsx/element.rs

@@ -43,11 +43,6 @@ impl Parse for Element {
     fn parse(stream: ParseStream) -> Result<Self> {
         let name = Ident::parse(stream)?;
 
-        // TODO: remove this in favor of the compile-time validation system
-        if !crate::util::is_valid_tag(&name.to_string()) {
-            return Err(Error::new(name.span(), "Not a valid Html tag"));
-        }
-
         // parse the guts
         let content: ParseBuffer;
         syn::braced!(content in stream);

+ 1 - 1
packages/core/examples/alternative.rs

@@ -4,7 +4,7 @@ fn main() {}
 // use dioxus_core as dioxus;
 // use dioxus_core::prelude::*;
 
-// static Example: FC<()> = |cx| {
+// pub static Example: FC<()> = |cx| {
 //     let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
 
 //     cx.render(LazyNodes::new(move |cx| {

+ 1 - 5
packages/core/examples/contextapi.rs

@@ -5,12 +5,8 @@ struct SomeContext {
     items: Vec<String>,
 }
 
-struct Props {
-    name: String,
-}
-
 #[allow(unused)]
-static Example: FC<Props> = |cpt| {
+static Example: FC<()> = |cpt| {
     todo!()
 
     // let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap());

+ 13 - 2
packages/core/src/context.rs

@@ -321,7 +321,10 @@ Any function prefixed with "use" should not be called conditionally.
     ///
     ///
     ///
-    pub fn use_task<Out, Fut, Init>(&self, task_initializer: Init) -> (&TaskHandle, &Option<Out>)
+    pub fn use_task<Out, Fut, Init>(
+        self,
+        task_initializer: Init,
+    ) -> (TaskHandle<'src>, &'src Option<Out>)
     where
         Out: 'static,
         Fut: Future<Output = Out> + 'static,
@@ -363,7 +366,7 @@ Any function prefixed with "use" should not be called conditionally.
                 if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
                     hook.value = Some(val);
                 }
-                (&TaskHandle { _p: PhantomData }, &hook.value)
+                (TaskHandle { _p: PhantomData }, &hook.value)
             },
             |_| {},
         )
@@ -463,6 +466,14 @@ impl<'src, P> Context<'src, P> {
     }
 }
 
+#[derive(Clone, Copy)]
 pub struct TaskHandle<'src> {
     _p: PhantomData<&'src ()>,
 }
+
+impl<'src> TaskHandle<'src> {
+    pub fn toggle(&self) {}
+    pub fn start(&self) {}
+    pub fn stop(&self) {}
+    pub fn restart(&self) {}
+}

+ 1 - 1
packages/core/src/nodes.rs

@@ -266,7 +266,7 @@ impl<'a> NodeFactory<'a> {
         }
     }
 
-    pub fn virtual_child<P>(
+    pub fn component<P>(
         &self,
         component: FC<P>,
         props: P,

+ 53 - 0
packages/hooks/src/usestate.rs

@@ -123,7 +123,14 @@ impl<'a, T: 'static> UseState<'a, T> {
         let slot = self.inner.wip.clone();
         Rc::new(move |new| *slot.borrow_mut() = Some(new))
     }
+
+    pub fn for_async(&self) -> AsyncUseState<T> {
+        AsyncUseState {
+            wip: self.inner.wip.clone(),
+        }
+    }
 }
+
 impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
     pub fn get_mut(self) -> RefMut<'a, T> {
         // make sure we get processed
@@ -175,9 +182,55 @@ impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
     }
 }
 
+/// MUL
+impl<'a, T: Copy + Mul<T, Output = T>> Mul<T> for UseState<'a, T> {
+    type Output = T;
+
+    fn mul(self, rhs: T) -> Self::Output {
+        self.inner.current_val.mul(rhs)
+    }
+}
+impl<'a, T: Copy + Mul<T, Output = T>> MulAssign<T> for UseState<'a, T> {
+    fn mul_assign(&mut self, rhs: T) {
+        self.set(self.inner.current_val.mul(rhs));
+    }
+}
+/// DIV
+impl<'a, T: Copy + Div<T, Output = T>> Div<T> for UseState<'a, T> {
+    type Output = T;
+
+    fn div(self, rhs: T) -> Self::Output {
+        self.inner.current_val.div(rhs)
+    }
+}
+impl<'a, T: Copy + Div<T, Output = T>> DivAssign<T> for UseState<'a, T> {
+    fn div_assign(&mut self, rhs: T) {
+        self.set(self.inner.current_val.div(rhs));
+    }
+}
+
 // enable displaty for the handle
 impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "{}", self.inner.current_val)
     }
 }
+
+/// A less ergonmic but still capable form of use_state that's valid for `static lifetime
+pub struct AsyncUseState<T: 'static> {
+    wip: Rc<RefCell<Option<T>>>,
+}
+
+impl<T: ToOwned> AsyncUseState<T> {
+    pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> {
+        // make sure we get processed
+        // self.needs_update();
+
+        // Bring out the new value, cloning if it we need to
+        // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
+        RefMut::map(self.wip.borrow_mut(), |slot| {
+            //
+            slot.as_mut().unwrap()
+        })
+    }
+}

+ 3 - 0
packages/html/src/lib.rs

@@ -924,6 +924,8 @@ builder_constructors! {
     /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
     /// element.
     ///
+    /// Part of the HTML namespace. Only works in HTML-compatible renderers
+    ///
     /// ## Definition and Usage
     /// - The <div> tag defines a division or a section in an HTML document.
     /// - The <div> tag is used as a container for HTML elements - which is then styled with CSS or manipulated with  JavaScript.
@@ -1575,6 +1577,7 @@ builder_constructors! {
     /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
     /// element.
     select {
+        value: String,
         autocomplete: String,
         autofocus: Bool,
         disabled: Bool,

+ 3 - 2
packages/mobile/src/lib.rs

@@ -9,7 +9,8 @@ use wry::application::window::Fullscreen;
 use wry::application::{
     dpi::LogicalSize,
     event::StartCause,
-    platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
+    // platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
+    // platform::ios::{ScreenEdge, WindowBuilderExtIOS, WindowExtIOS},
 };
 use wry::webview::WebViewBuilder;
 use wry::{
@@ -23,7 +24,7 @@ fn init_logging() {
     simple_logger::SimpleLogger::new().init().unwrap();
 }
 
-static HTML_CONTENT: &'static str = include_str!("./../../webview/src/index.html");
+static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html");
 
 pub fn launch(root: FC<()>, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> {
     launch_with_props(root, (), builder)

+ 1 - 1
packages/ssr/examples/tide.rs

@@ -38,7 +38,7 @@ struct ExampleProps {
     initial_name: String,
 }
 
-static Example: FC<ExampleProps> = |cx| {
+pub static Example: FC<ExampleProps> = |cx| {
     let dispaly_name = use_state(cx, move || cx.initial_name.clone());
 
     cx.render(rsx! {

+ 6 - 6
packages/ssr/examples/tofile.rs

@@ -19,7 +19,7 @@ fn main() {
         .unwrap();
 }
 
-pub const App: FC<()> = |cx| {
+pub static App: FC<()> = |cx| {
     cx.render(rsx!(
         div { class: "overflow-hidden"
             link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -34,7 +34,7 @@ pub const App: FC<()> = |cx| {
     ))
 };
 
-pub const Header: FC<()> = |cx| {
+pub static Header: FC<()> = |cx| {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font"
@@ -60,7 +60,7 @@ pub const Header: FC<()> = |cx| {
     })
 };
 
-pub const Hero: FC<()> = |cx| {
+pub static Hero: FC<()> = |cx| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -98,7 +98,7 @@ pub const Hero: FC<()> = |cx| {
         }
     })
 };
-pub const Entry: FC<()> = |cx| {
+pub static Entry: FC<()> = |cx| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -111,7 +111,7 @@ pub const Entry: FC<()> = |cx| {
     })
 };
 
-pub const StacksIcon: FC<()> = |cx| {
+pub static StacksIcon: FC<()> = |cx| {
     cx.render(rsx!(
         svg {
             // xmlns: "http://www.w3.org/2000/svg"
@@ -126,7 +126,7 @@ pub const StacksIcon: FC<()> = |cx| {
         }
     ))
 };
-pub const RightArrowIcon: FC<()> = |cx| {
+pub static RightArrowIcon: FC<()> = |cx| {
     cx.render(rsx!(
         svg {
             fill: "none"

+ 6 - 6
packages/web/examples/demo.rs

@@ -25,7 +25,7 @@ fn main() {
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
 }
 
-pub const App: FC<()> = |cx| {
+pub static App: FC<()> = |cx| {
     cx.render(rsx!(
         div { class: "overflow-hidden"
             link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -40,7 +40,7 @@ pub const App: FC<()> = |cx| {
     ))
 };
 
-pub const Header: FC<()> = |cx| {
+pub static Header: FC<()> = |cx| {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font"
@@ -66,7 +66,7 @@ pub const Header: FC<()> = |cx| {
     })
 };
 
-pub const Hero: FC<()> = |cx| {
+pub static Hero: FC<()> = |cx| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -104,7 +104,7 @@ pub const Hero: FC<()> = |cx| {
         }
     })
 };
-pub const Entry: FC<()> = |cx| {
+pub static Entry: FC<()> = |cx| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -117,7 +117,7 @@ pub const Entry: FC<()> = |cx| {
     })
 };
 
-pub const StacksIcon: FC<()> = |cx| {
+pub static StacksIcon: FC<()> = |cx| {
     cx.render(rsx!(
         svg {
             // xmlns: "http://www.w3.org/2000/svg"
@@ -132,7 +132,7 @@ pub const StacksIcon: FC<()> = |cx| {
         }
     ))
 };
-pub const RightArrowIcon: FC<()> = |cx| {
+pub static RightArrowIcon: FC<()> = |cx| {
     cx.render(rsx!(
         svg {
             fill: "none"

+ 4 - 2
src/lib.rs

@@ -93,7 +93,7 @@
 //! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component.
 //!
 //! ```
-//! static Example: FC<()> = |cx| {
+//! pub static Example: FC<()> = |cx| {
 //!     let (val, set_val) = use_state(cx, || 0);
 //!     cx.render(rsx!(
 //!         button { onclick: move |_| set_val(val + 1) }
@@ -156,7 +156,7 @@
 //!     diouxs::web::launch(Example);
 //! }
 //!
-//! static Example: FC<()> = |cx| {
+//! pub static Example: FC<()> = |cx| {
 //!     cx.render(rsx! {
 //!         div { "Hello World!" }
 //!     })
@@ -188,6 +188,8 @@ pub use dioxus_hooks as hooks;
 #[cfg(feature = "desktop")]
 pub use dioxus_desktop as desktop;
 
+pub mod debug {}
+
 pub mod prelude {
     //! A glob import that includes helper types like FC, rsx!, html!, and required traits
     pub use dioxus_core::prelude::*;