Browse Source

docs: remove all usages of static closure syntax and update readme

Jonathan Kelley 3 năm trước cách đây
mục cha
commit
cafb7df736
66 tập tin đã thay đổi với 789 bổ sung2016 xóa
  1. 19 9
      README.md
  2. 0 5
      docs/guide/src/elements/conditional_rendering.md
  3. 0 6
      docs/guide/src/hello_world.md
  4. 1 1
      examples/README.md
  5. 23 18
      examples/async.rs
  6. 2 2
      examples/borrowed.rs
  7. 1 1
      examples/calculator.rs
  8. 0 33
      examples/core/alternative.rs
  9. 0 108
      examples/core/html.rs
  10. 0 118
      examples/core/jsframework.rs
  11. 22 18
      examples/core_reference/antipatterns.rs
  12. 7 6
      examples/core_reference/basics.rs
  13. 10 7
      examples/core_reference/children.rs
  14. 19 21
      examples/core_reference/conditional_rendering.rs
  15. 6 6
      examples/core_reference/controlled_inputs.rs
  16. 2 2
      examples/core_reference/custom_elements.rs
  17. 3 1
      examples/core_reference/empty.rs
  18. 20 16
      examples/core_reference/errorhandling.rs
  19. 14 14
      examples/core_reference/fragments.rs
  20. 2 2
      examples/core_reference/global_css.rs
  21. 4 4
      examples/core_reference/inline_styles.rs
  22. 11 16
      examples/core_reference/iterators.rs
  23. 21 22
      examples/core_reference/listener.rs
  24. 7 7
      examples/core_reference/memo.rs
  25. 0 12
      examples/core_reference/noderefs.rs
  26. 0 10
      examples/core_reference/signals.rs
  27. 9 7
      examples/core_reference/spreadpattern.rs
  28. 0 10
      examples/core_reference/statemanagement.rs
  29. 2 2
      examples/core_reference/suspense.rs
  30. 3 3
      examples/core_reference/task.rs
  31. 0 9
      examples/core_reference/testing.rs
  32. 0 25
      examples/core_reference/tostring.rs
  33. 3 3
      examples/coroutine.rs
  34. 1 2
      examples/crm.rs
  35. 0 5
      examples/desktop/core.rs
  36. 0 109
      examples/desktop/crm.rs
  37. 0 18
      examples/desktop/demo.rs
  38. 0 379
      examples/desktop/todomvc.css
  39. 0 170
      examples/desktop/todomvc.rs
  40. 16 8
      examples/framework_benchmark.rs
  41. 5 4
      examples/hydration.rs
  42. 3 3
      examples/inputs.rs
  43. 1 1
      examples/manual_edits.rs
  44. 27 24
      examples/pattern_model.rs
  45. 3 3
      examples/pattern_reducer.rs
  46. 3 3
      examples/readme.rs
  47. 2 2
      examples/router.rs
  48. 3 0
      examples/rsx_compile_fail.rs
  49. 9 5
      examples/rsx_usage.rs
  50. 3 3
      examples/ssr.rs
  51. 5 4
      examples/ssr/basic.rs
  52. 0 10
      examples/ssr/hydration.rs
  53. 0 298
      examples/ssr/template.html
  54. 0 61
      examples/ssr/tide.rs
  55. 14 12
      examples/ssr/tofile.rs
  56. 12 12
      examples/tailwind.rs
  57. 7 6
      examples/tasks.rs
  58. 49 45
      examples/todomvc.rs
  59. 13 13
      examples/weather_app.rs
  60. 3 3
      examples/web_tick.rs
  61. 7 11
      examples/xss_safety.rs
  62. 1 1
      packages/core-macro/src/inlineprops.rs
  63. 0 277
      packages/hooks/src/usestate.rs
  64. 211 0
      packages/hooks/src/usestate/handle.rs
  65. 78 0
      packages/hooks/src/usestate/mod.rs
  66. 102 0
      packages/hooks/src/usestate/owned.rs

+ 19 - 9
README.md

@@ -22,19 +22,24 @@
       alt="docs.rs docs" />
   </a>
   <!-- CI -->
-  <a href="https://github.com/jkelleyrtp/dioxus/actions">
+  <!-- <a href="https://github.com/jkelleyrtp/dioxus/actions">
     <img src="https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg"
       alt="CI status" />
+  </a> -->
+  <!--Awesome -->
+  <a href="https://github.com/dioxuslabs/awesome-dioxus">
+    <img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg"
+      alt="Awesome Page" />
   </a>
 </div>
 
 <div align="center">
   <h3>
-    <a href="https://dioxuslabs.com/guide"> Guide </a>
-    <span> | </span>
     <a href="https://dioxuslabs.com"> Website </a>
     <span> | </span>
-    <a href="https://github.com/DioxusLabs/awesome-dioxus"> Examples </a>
+    <a href="https://dioxuslabs.com/guide"> Guide </a>
+    <span> | </span>
+    <a href="https://github.com/DioxusLabs/example-projects"> Examples </a>
   </h3>
 </div>
 
@@ -66,6 +71,15 @@ If you know React, then you already know Dioxus.
 - Multi-channel asynchronous scheduler for first-class async support.
 - And more! Read the [full release post here](https://dioxuslabs.com/).
 
+
+### Examples
+
+All examples in this repo are desktop apps. To run an example, simply clone this repo and use cargo with the `desktop` feature enabled. For SSR examples, you might need to enable SSR instead.
+
+```
+cargo run --features desktop --example EXAMPLE
+```
+
 ## Get Started with...
 
 <table style="width:100%" align="center">
@@ -80,8 +94,7 @@ If you know React, then you already know Dioxus.
 </table>
 
 
-
-## Examples Projects:
+## Example Projects:
 
 | File Navigator (Desktop)                                                                                                                                                        | WiFi scanner (Desktop)                                                                                                                                                                 | TodoMVC (All platforms)                                                                                                                                                 | E-commerce w/ Tailwind (Liveview)                                                                                                                                                     |
 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -90,9 +103,6 @@ If you know React, then you already know Dioxus.
 
 See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem.
 
-## Running examples locally
-
-All local examples are built for the desktop renderer. This means you can simply clone this repo and call `cargo run --example EXAMPLE_NAME`. To run non-desktop examples, checkout the example projects shown above.
 
 ## Why Dioxus and why Rust?
 

+ 0 - 5
docs/guide/src/elements/conditional_rendering.md

@@ -81,11 +81,6 @@ fn App(cx: Scope)-> Element {
 }
 ```
 
-This syntax even enables us to write a one-line component:
-```rust
-static App: Component = |cx| rsx!(cx, "hello world!");
-```
-
 Alternatively, for match statements, we can just return the builder itself and pass it into a final, single call to `cx.render`:
 
 ```rust

+ 0 - 6
docs/guide/src/hello_world.md

@@ -132,12 +132,6 @@ fn App(cx: Scope) -> Element {
 }
 ```
 
-Writing `fn App(cx: Scope) -> Element {` might become tedious. Rust will also let you write functions as static closures, but these types of Components cannot have props that borrow data.
-
-```rust
-static App: Component = |cx| cx.render(rsx!(div { "Hello, world!" }));
-```
-
 ### What is this `Scope` object?
 
 Coming from React, the `Scope` object might be confusing. In React, you'll want to store data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering. 

+ 1 - 1
examples/README.md

@@ -115,7 +115,7 @@ fn App(cx: Scope) -> Element {
 
 Tiny components:
 ```rust
-static App: Component = |cx, _| rsx!(cx, div {"hello world!"});
+static App: Component = |cx| rsx!(cx, div {"hello world!"});
 ```
 
 Borrowed prop contents:

+ 23 - 18
examples/async.rs

@@ -17,27 +17,32 @@ fn app(cx: Scope) -> Element {
 
     let (async_count, dir) = (count.for_async(), *direction);
 
-    let task = use_coroutine(&cx, move || async move {
-        loop {
-            TimeoutFuture::new(250).await;
-            *async_count.modify() += dir;
+    let task = use_coroutine(&cx, move || {
+        //
+        async move {
+            loop {
+                TimeoutFuture::new(250).await;
+                // *async_count.modify() += dir;
+            }
         }
     });
 
-    rsx!(cx, div {
-        h1 {"count is {count}"}
-        button { onclick: move |_| task.stop(),
-            "Stop counting"
-        }
-        button { onclick: move |_| task.resume(),
-            "Start counting"
-        }
-        button {
-            onclick: move |_| {
-                *direction.modify() *= -1;
-                task.restart();
-            },
-            "Switch counting direcion"
+    cx.render(rsx! {
+        div {
+            h1 {"count is {count}"}
+            button { onclick: move |_| task.stop(),
+                "Stop counting"
+            }
+            button { onclick: move |_| task.resume(),
+                "Start counting"
+            }
+            button {
+                onclick: move |_| {
+                    *direction.modify() *= -1;
+                    task.restart();
+                },
+                "Switch counting direcion"
+            }
         }
     })
 }

+ 2 - 2
examples/borrowed.rs

@@ -17,10 +17,10 @@ and is proven to be safe with MIRI.
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
-fn App(cx: Scope) -> Element {
+fn app(cx: Scope) -> Element {
     let text = cx.use_hook(|_| vec![String::from("abc=def")]);
 
     let first = text.get_mut(0).unwrap();

+ 1 - 1
examples/calculator.rs

@@ -12,7 +12,7 @@ use dioxus::prelude::*;
 use separator::Separatable;
 
 fn main() {
-    // dioxus::desktop::launch(app);
+    dioxus::desktop::launch(app);
 }
 
 fn app(cx: Scope) -> Element {

+ 0 - 33
examples/core/alternative.rs

@@ -1,33 +0,0 @@
-use dioxus_core::prelude::*;
-use dioxus_core_macro::format_args_f;
-use dioxus_core_macro::rsx;
-use dioxus_html as dioxus_elements;
-
-fn main() {
-    let mut dom = VirtualDom::new(EXAMPLE);
-    dom.rebuild();
-    println!("{}", dom);
-}
-
-pub static EXAMPLE: Component = |cx| {
-    let list = (0..10).map(|_f| {
-        rsx! {
-            "{_f}"
-        }
-    });
-
-    cx.render(Some(LazyNodes::new(move |cx| {
-        cx.raw_element(
-            "div",
-            None,
-            [],
-            [],
-            [
-                cx.text(format_args!("hello")),
-                cx.text(format_args!("hello")),
-                cx.fragment_from_iter(list),
-            ],
-            None,
-        )
-    })))
-};

+ 0 - 108
examples/core/html.rs

@@ -1,108 +0,0 @@
-use bumpalo::Bump;
-
-fn main() {}
-
-fn build(factory: Factory) {
-    factory.text();
-    div::new(factory)
-        .r#class()
-        .r#tag()
-        .r#type()
-        .add_children()
-        .iter_children()
-        .finish();
-}
-
-/// # The `div` element
-///
-///
-/// The <div> HTML element is the generic container for flow content. It has no effect on the content or layout until
-/// styled in some way using CSS (e.g. styling is directly applied to it, or some kind of layout model like Flexbox is
-/// applied to its parent element).
-///
-/// As a "pure" container, the <div> element does not inherently represent anything. Instead, it's used to group content
-/// so it can be easily styled using the class or id attributes, marking a section of a document as being written in a
-/// different language (using the lang attribute), and so on.
-///
-/// ## Usage
-/// ```
-/// rsx!{
-///     div { class: "tall", id: "unique id"
-///         h1 {}
-///         p {}
-///     }
-/// }
-///
-/// ```
-///
-/// ## Specifications
-/// - Content categories: Flow content, palpable content.
-/// - Permitted content: Flow content.
-/// - Permitted parents: Any element that accepts flow content.
-#[allow(non_camel_case_types)]
-struct div {}
-
-struct h1 {}
-
-struct h2 {}
-
-trait BasicElement: Sized {
-    const TagName: &'static str;
-    fn get_bump(&self) -> &Bump;
-    fn new(factory: Factory) -> Self;
-    fn add_children(self) -> Self {
-        self
-    }
-    fn iter_children(self) -> Self {
-        self
-    }
-    fn finish(self) -> Self {
-        self
-    }
-}
-
-impl BasicElement for div {
-    const TagName: &'static str = "div";
-
-    fn get_bump(&self) -> &Bump {
-        todo!()
-    }
-
-    fn new(_factory: Factory) -> Self {
-        todo!()
-    }
-}
-
-impl div {
-    fn class(self) -> Self {
-        self
-    }
-    fn tag(self) -> Self {
-        self
-    }
-    fn r#type(self) -> Self {
-        self
-    }
-}
-
-#[derive(Clone, Copy)]
-struct Factory<'a> {
-    bump: &'a bumpalo::Bump,
-}
-
-impl<'a> Factory<'a> {
-    fn text(&self) -> &str {
-        todo!()
-    }
-    fn new_el(&'a self, tag: &'static str) -> ElementBuilder<'a> {
-        ElementBuilder {
-            bump: self.bump,
-            tag,
-        }
-    }
-}
-
-struct ElementBuilder<'a> {
-    tag: &'static str,
-    bump: &'a bumpalo::Bump,
-}

+ 0 - 118
examples/core/jsframework.rs

@@ -1,118 +0,0 @@
-#![allow(non_snake_case)]
-
-use dioxus::component::Scope;
-use dioxus::events::on::MouseEvent;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_html as dioxus_elements;
-use rand::prelude::*;
-use std::fmt::Display;
-
-fn main() {
-    let mut dom = VirtualDom::new(App);
-    let g = dom.rebuild();
-    assert!(g.edits.len() > 1);
-}
-
-fn App((cx, props): Scope) -> Element {
-    let mut rng = SmallRng::from_entropy();
-    let rows = (0..10_000_usize).map(|f| {
-        let label = Label::new(&mut rng);
-        rsx! {
-            Row {
-                row_id: f,
-                label: label
-            }
-        }
-    });
-    cx.render(rsx! {
-        table {
-            tbody {
-                {rows}
-            }
-        }
-    })
-}
-
-#[derive(PartialEq, Props)]
-struct RowProps {
-    row_id: usize,
-    label: Label,
-}
-
-fn Row<'a>((cx, props): Scope<'a, RowProps>) -> Element<'a> {
-    let handler = move |evt: MouseEvent| {
-        let g = evt.button;
-    };
-    cx.render(rsx! {
-        tr {
-            // td { class:"col-md-1", "{cx.props.row_id}" }
-            // td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
-            //     a { class: "lbl", "{cx.props.label}" }
-            // }
-            // td { class: "col-md-1"
-            //     a { class: "remove", onclick: {handler}
-            //         span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
-            //     }
-            // }
-            // td { class: "col-md-6" }
-        }
-    })
-}
-
-#[derive(PartialEq)]
-struct Label([&'static str; 3]);
-
-impl Label {
-    fn new(rng: &mut SmallRng) -> Self {
-        Label([
-            ADJECTIVES.choose(rng).unwrap(),
-            COLOURS.choose(rng).unwrap(),
-            NOUNS.choose(rng).unwrap(),
-        ])
-    }
-}
-impl Display for Label {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{} {} {}", self.0[0], self.0[1], self.0[2])
-    }
-}
-
-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",
-];

+ 22 - 18
examples/core_reference/antipatterns.rs

@@ -32,16 +32,20 @@ use dioxus::prelude::*;
 struct NoKeysProps {
     data: std::collections::HashMap<u32, String>,
 }
-static AntipatternNoKeys: Component<NoKeysProps> = |cx| {
+fn AntipatternNoKeys(cx: Scope<NoKeysProps>) -> Element {
     // WRONG: Make sure to add keys!
-    rsx!(cx, ul {
-        {cx.props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
+    cx.render(rsx! {
+        ul {
+            cx.props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))
+        }
     });
     // RIGHT: Like this:
-    rsx!(cx, ul {
-        {cx.props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
+    cx.render(rsx! {
+        ul {
+            cx.props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))
+        }
     })
-};
+}
 
 /// Antipattern: Deeply nested fragments
 /// ------------------------------------
@@ -54,9 +58,9 @@ static AntipatternNoKeys: Component<NoKeysProps> = |cx| {
 ///
 /// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing
 /// an API for registering shared state without the ContextProvider pattern.
-static AntipatternNestedFragments: Component = |cx| {
+fn AntipatternNestedFragments(cx: Scope<()>) -> Element {
     // Try to avoid heavily nesting fragments
-    rsx!(cx,
+    cx.render(rsx!(
         Fragment {
             Fragment {
                 Fragment {
@@ -68,8 +72,8 @@ static AntipatternNestedFragments: Component = |cx| {
                 }
             }
         }
-    )
-};
+    ))
+}
 
 /// Antipattern: Using state after it's been updated
 /// -----------------------------------------------
@@ -82,13 +86,13 @@ static AntipatternNestedFragments: Component = |cx| {
 /// However, calling set_state will *not* update the current version of state in the component. This should be easy to
 /// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state`
 /// merely places a new value in the queue and schedules the component for a future update.
-static AntipatternRelyingOnSetState: Component = |cx| {
+fn AntipatternRelyingOnSetState(cx: Scope) -> Element {
     let (state, set_state) = use_state(&cx, || "Hello world").classic();
     set_state("New state");
     // This will return false! `state` will *still* be "Hello world"
     assert!(state == &"New state");
     todo!()
-};
+}
 
 /// Antipattern: Capitalization
 /// ---------------------------
@@ -120,16 +124,16 @@ static antipattern_component: Component = |cx| todo!();
 struct MisuedHooksProps {
     should_render_state: bool,
 }
-static AntipatternMisusedHooks: Component<MisuedHooksProps> = |cx| {
+fn AntipatternMisusedHooks(cx: Scope<MisuedHooksProps>) -> Element {
     if props.should_render_state {
         // do not place a hook in the conditional!
         // prefer to move it out of the conditional
         let (state, set_state) = use_state(&cx, || "hello world").classic();
-        rsx!(cx, div { "{state}" })
+        cx.render(rsx!(div { "{state}" }))
     } else {
-        rsx!(cx, div { "Not rendering state" })
+        cx.render(rsx!(div { "Not rendering state" }))
     }
-};
+}
 
 /// Antipattern: Downcasting refs and panicking
 /// ------------------------------------------
@@ -173,9 +177,9 @@ static _example: Component = |cx| todo!();
 /// libraries.
 static __example: Component = |cx| todo!();
 
-pub static Example: Component = |cx| {
+fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         AntipatternNoKeys { data: std::collections::HashMap::new() }
         AntipatternNestedFragments {}
     })
-};
+}

+ 7 - 6
examples/core_reference/basics.rs

@@ -9,7 +9,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
             Greeting {
@@ -18,20 +18,21 @@ pub static Example: Component = |cx| {
             }
         }
     })
-};
+}
 
 #[derive(PartialEq, Props)]
-struct GreetingProps {
+struct GreetingProps<'a> {
     name: &'static str,
+    children: Element<'a>,
 }
 
-static Greeting: Component<GreetingProps> = |cx| {
+pub fn Greeting<'a>(cx: Scope<'a, GreetingProps<'a>>) -> Element {
     cx.render(rsx! {
         div {
             h1 { "Hello, {cx.props.name}!" }
             p { "Welcome to the Dioxus framework" }
             br {}
-            {cx.children()}
+            &cx.props.children
         }
     })
-};
+}

+ 10 - 7
examples/core_reference/children.rs

@@ -18,7 +18,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: Component = |cx| {
+fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
             Banner {
@@ -29,16 +29,19 @@ pub static Example: Component = |cx| {
             }
         }
     })
-};
+}
 
-pub static Banner: Component = |cx| {
+#[derive(Props)]
+struct BannerProps {
+    children: Element<'a>,
+}
+
+fn Banner(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
             h1 { "This is a great banner!" }
-            div { class: "content"
-                {cx.children()}
-            }
+            div { class: "content", &cx.props.children }
             footer { "Wow, what a great footer" }
         }
     })
-};
+}

+ 19 - 21
examples/core_reference/conditional_rendering.rs

@@ -16,15 +16,15 @@ use dioxus::prelude::*;
 pub struct MyProps {
     should_show: bool,
 }
-pub static Example0: Component<MyProps> = |cx| {
+pub fn Example0(cx: Scope<MyProps>) -> Element {
     cx.render(rsx! {
         div {
-            {cx.props.should_show.then(|| rsx!{
+            cx.props.should_show.then(|| rsx!{
                 h1 { "showing the title!" }
-            })}
+            })
         }
     })
-};
+}
 
 // Convert a boolean conditional into an either/or
 // Because rsx! is lazy (produces a closure), we cannot use it in two branch arms. To use it in matching/branching, we
@@ -39,31 +39,29 @@ pub static Example0: Component<MyProps> = |cx| {
 pub struct MyProps1 {
     should_show: bool,
 }
-pub static Example1: Component<MyProps1> = |cx| {
+pub fn Example1(cx: Scope<MyProps1>) -> Element {
     cx.render(rsx! {
         div {
             // With matching
-            {match props.should_show {
+            match props.should_show {
                 true => cx.render(rsx!(div {"it is true!"})),
                 false => rsx!(cx, div {"it is false!"}),
-            }}
+            },
 
             // or with just regular conditions
-            {if props.should_show {
+            if props.should_show {
                 rsx!(cx, div {"it is true!"})
             } else {
                 rsx!(cx, div {"it is false!"})
-            }}
+            },
 
             // or with optional chaining
-            {
-                props.should_show
+            props.should_show
                 .then(|| rsx!(cx, div {"it is true!"}))
                 .unwrap_or_else(|| rsx!(cx, div {"it is false!"}))
-            }
         }
     })
-};
+}
 
 /// Matching can be expanded
 
@@ -77,19 +75,19 @@ pub enum Color {
 pub struct MyProps2 {
     color: Color,
 }
-pub static Example2: Component<MyProps2> = |cx| {
+pub fn Example2(cx: Scope<MyProps2>) -> Element {
     cx.render(rsx! {
         div {
-            {match props.color {
+            match props.color {
                 Color::Green => rsx!(cx, div {"it is Green!"}),
                 Color::Yellow => rsx!(cx, div {"it is Yellow!"}),
                 Color::Red => rsx!(cx, div {"it is Red!"}),
-            }}
+            }
         }
     })
-};
+}
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope<()>) -> Element {
     let should_show = use_state(&cx, || false);
     let mut color_index = use_state(&cx, || 0);
     let color = match *color_index % 2 {
@@ -101,11 +99,11 @@ pub static Example: Component = |cx| {
     cx.render(rsx! {
         div {
             button {
-                onclick: move |_| should_show.set(!*should_show)
+                onclick: move |_| should_show.set(!*should_show),
                 "click to toggle showing the examples"
             }
             button {
-                onclick: move |_| color_index += 1
+                onclick: move |_| color_index += 1,
                 "click to for the enxt color"
             }
             Example0 { should_show: *should_show }
@@ -113,4 +111,4 @@ pub static Example: Component = |cx| {
             Example2 { color: color }
         }
     })
-};
+}

+ 6 - 6
examples/core_reference/controlled_inputs.rs

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

+ 2 - 2
examples/core_reference/custom_elements.rs

@@ -11,7 +11,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
             custom_element {
@@ -19,7 +19,7 @@ pub static Example: Component = |cx| {
             }
         }
     })
-};
+}
 
 mod dioxus_elements {
     use std::fmt::Arguments;

+ 3 - 1
examples/core_reference/empty.rs

@@ -5,4 +5,6 @@
 
 use dioxus::prelude::*;
 
-pub static Example: Component = |cx| cx.render(rsx! { Fragment {} });
+pub fn Example(cx: Scope) -> Element {
+    None
+}

+ 20 - 16
examples/core_reference/errorhandling.rs

@@ -23,20 +23,20 @@ fn main() {}
 /// This is one way to go about error handling (just toss things away with unwrap).
 /// However, if you get it wrong, the whole app will crash.
 /// This is pretty flimsy.
-static App: Component = |cx| {
+pub fn App(cx: Scope) -> Element {
     let data = get_data().unwrap();
     cx.render(rsx!( div { "{data}" } ))
-};
+}
 
 /// This is a pretty verbose way of error handling
 /// However, it's still pretty good since we don't panic, just fail to render anything
-static App1: Component = |cx| {
+pub fn App1(cx: Scope) -> Element {
     let data = match get_data() {
         Some(data) => data,
         None => return None,
     };
     cx.render(rsx!( div { "{data}" } ))
-};
+}
 
 /// This is an even better form of error handling.
 /// However, it _does_ make the component go blank, which might not be desirable.
@@ -46,33 +46,37 @@ static App1: Component = |cx| {
 /// a user is logged in.
 ///
 /// Dioxus will throw an error in the console if the None-path is ever taken.
-static App2: Component = |cx| {
+pub fn App2(cx: Scope) -> Element {
     let data = get_data()?;
     cx.render(rsx!( div { "{data}" } ))
-};
+}
 
 /// This is top-tier error handling since it displays a failure state.
 ///
 /// However, the error is lacking in context.
-static App3: Component = |cx| match get_data() {
-    Some(data) => cx.render(rsx!( div { "{data}" } )),
-    None => cx.render(rsx!( div { "Failed to load data :(" } )),
-};
+pub fn App3(cx: Scope) -> Element {
+    match get_data() {
+        Some(data) => cx.render(rsx!( div { "{data}" } )),
+        None => cx.render(rsx!( div { "Failed to load data :(" } )),
+    }
+}
 
 /// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts
 /// a Result<T, V> into an Option<T> and lets you abort rendering by early-returning `None`
-static App4: Component = |cx| {
+pub fn App4(cx: Scope) -> Element {
     let data = get_data_err().ok()?;
     cx.render(rsx!( div { "{data}" } ))
-};
+}
 
 /// This is great error handling since it displays a failure state... with context!
 ///
 /// Hopefully you'll never need to display a screen like this. It's rather bad taste
-static App5: Component = |cx| match get_data_err() {
-    Ok(data) => cx.render(rsx!( div { "{data}" } )),
-    Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
-};
+pub fn App5(cx: Scope) -> Element {
+    match get_data_err() {
+        Ok(data) => cx.render(rsx!( div { "{data}" } )),
+        Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
+    }
+}
 
 // this fetching function produces "nothing"
 fn get_data() -> Option<String> {

+ 14 - 14
examples/core_reference/fragments.rs

@@ -10,17 +10,25 @@
 
 use dioxus::prelude::*;
 
+pub fn Example(cx: Scope) -> Element {
+    cx.render(rsx! {
+        App1 {}
+        App2 {}
+        App3 {}
+    })
+}
+
 // Returning multiple elements with rsx! or html!
-static App1: Component = |cx| {
+pub fn App1(cx: Scope) -> Element {
     cx.render(rsx! {
         h1 { }
         h2 { }
         h3 { }
     })
-};
+}
 
 // Using the Fragment component
-static App2: Component = |cx| {
+pub fn App2(cx: Scope) -> Element {
     cx.render(rsx! {
         Fragment {
             div {}
@@ -28,10 +36,10 @@ static App2: Component = |cx| {
             "asd"
         }
     })
-};
+}
 
 // Using the `fragment` method on the NodeFactory
-static App3: Component = |cx| {
+pub fn App3(cx: Scope) -> Element {
     cx.render(LazyNodes::new(move |fac| {
         fac.fragment_from_iter([
             fac.text(format_args!("A")),
@@ -40,12 +48,4 @@ static App3: Component = |cx| {
             fac.text(format_args!("B")),
         ])
     }))
-};
-
-pub static Example: Component = |cx| {
-    cx.render(rsx! {
-        App1 {}
-        App2 {}
-        App3 {}
-    })
-};
+}

+ 2 - 2
examples/core_reference/global_css.rs

@@ -19,7 +19,7 @@ h1   {color: blue;}
 p    {color: red;}
 "#;
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         head { style { "{STYLE}" } }
         body {
@@ -27,4 +27,4 @@ pub static Example: Component = |cx| {
             p {"This is a paragraph"}
         }
     })
-};
+}

+ 4 - 4
examples/core_reference/inline_styles.rs

@@ -10,7 +10,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         head {
             style: { background_color: "powderblue" }
@@ -24,15 +24,15 @@ pub static Example: Component = |cx| {
             }
         }
     })
-};
+}
 
 // .... technically the rsx! macro is slightly broken at the moment and allows 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: Component = |cx| {
+pub fn Example2(cx: Scope) -> Element {
     cx.render(rsx! {
         div { color: "red"
             "hello world!"
         }
     })
-};
+}

+ 11 - 16
examples/core_reference/iterators.rs

@@ -15,25 +15,20 @@ use dioxus::prelude::*;
 pub static Example: Component = |cx| {
     let example_data = use_state(&cx, || 0);
 
-    let v = (0..10).map(|f| {
-        rsx! {
-            li { onclick: move |_| example_data.set(f)
-                "ID: {f}"
-                ul {
-                    (0..10).map(|k| rsx!{
-                        li {
-                            "Sub iterator: {f}.{k}"
-                        }
-                    })
-                }
-            }
-        }
-    });
-
     cx.render(rsx! {
         h3 {"Selected: {example_data}"}
         ul {
-            {v}
+            (0..10).map(|f| rsx! {
+                li {
+                    onclick: move |_| example_data.set(f),
+                    "ID: {f}"
+                    ul {
+                        (0..10).map(|k| rsx!{
+                            li { "Sub iterator: {f}.{k}" }
+                        })
+                    }
+                }
+            })
         }
     })
 };

+ 21 - 22
examples/core_reference/listener.rs

@@ -7,50 +7,49 @@
 
 use dioxus::prelude::*;
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         ButtonList {}
         NonUpdatingEvents {}
         DisablePropagation {}
     })
-};
+}
 
 /// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
-static ButtonList: Component = |cx| {
+pub fn ButtonList(cx: Scope) -> Element {
     let name = use_state(&cx, || "...?");
 
-    let names = ["jack", "jill", "john", "jane"]
-        .iter()
-        .map(move |n| rsx!(button { onclick: move |_| name.set(n), "{n}" }));
-
     cx.render(rsx!(
         div {
             h1 { "Hello, {name}" }
-            {names}
+
+            ["jack", "jill", "john", "jane"]
+                .iter()
+                .map(move |n| rsx!(button { onclick: move |_| name.set(n), "{n}" }))
         }
     ))
-};
+}
 
 /// This shows how listeners may be without a visible change in the display.
 /// Check the console.
-static NonUpdatingEvents: Component = |cx| {
-    rsx!(cx, div {
-        button {
-            onclick: move |_| log::info!("Did not cause any updates!")
-            "Click me to log!"
+pub fn NonUpdatingEvents(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            button {
+                onclick: move |_| log::info!("Did not cause any updates!"),
+                "Click me to log!"
+            }
         }
     })
-};
+}
 
-static DisablePropagation: Component = |cx| {
-    rsx!(cx,
+pub fn DisablePropagation(cx: Scope) -> Element {
+    cx.render(rsx! {
         div {
             onclick: move |_| log::info!("event propagated to the div!")
             button {
-                onclick: move |evt| {
-                    log::info!("Button will allow propagation");
-                }
+                onclick: move |evt| log::info!("Button will allow propagation"),
             }
         }
-    )
-};
+    })
+}

+ 7 - 7
examples/core_reference/memo.rs

@@ -21,11 +21,11 @@ use dioxus::prelude::*;
 
 // By default, components with no props are always memoized.
 // A props of () is considered empty.
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     cx.render(rsx! {
         div { "100% memoized!" }
     })
-};
+}
 
 // These props do not borrow any content, and therefore can be safely memoized.
 // However, the parent *must* create a new string on every render.
@@ -35,11 +35,11 @@ pub struct MyProps1 {
     name: String,
 }
 
-pub static Example1: Component<MyProps1> = |cx| {
+pub fn Example1(cx: Scope<MyProps1>) -> Element {
     cx.render(rsx! {
         div { "100% memoized! {cx.props.name}" }
     })
-};
+}
 
 // These props do not borrow any content, and therefore can be safely memoized.
 // In contrast with the `String` example, these props use `Rc<str>` which operates similar to strings in JavaScript.
@@ -49,11 +49,11 @@ pub struct MyProps2 {
     name: std::rc::Rc<str>,
 }
 
-pub static Example2: Component<MyProps2> = |cx| {
+pub fn Example2(cx: Scope<MyProps2>) -> Element {
     cx.render(rsx! {
         div { "100% memoized! {cx.props.name}" }
     })
-};
+}
 
 // These props *do* borrow any content, and therefore cannot be safely memoized!.
 #[derive(PartialEq, Props)]
@@ -61,7 +61,7 @@ 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 `pub static Example: Component` pattern _will_ specify a lifetime, but that lifetime will be static which might
+// Using the `pub fn Example(cx: Scope): Component` pattern _will_ specify a lifetime, but that lifetime will be static which might
 // not exactly be what you want
 fn Example3(cx: Scope<'a, MyProps3<'a>>) -> DomTree {
     cx.render(rsx! {

+ 0 - 12
examples/core_reference/noderefs.rs

@@ -1,12 +0,0 @@
-use dioxus::prelude::*;
-fn main() {}
-
-pub static Example: Component = |cx| {
-    let p = 10;
-
-    cx.render(rsx! {
-        div {
-
-        }
-    })
-};

+ 0 - 10
examples/core_reference/signals.rs

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

+ 9 - 7
examples/core_reference/spreadpattern.rs

@@ -9,7 +9,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     let props = MyProps {
         count: 0,
         live: true,
@@ -18,22 +18,24 @@ pub static Example: Component = |cx| {
     cx.render(rsx! {
         Example1 { ..props, count: 10, div {"child"} }
     })
-};
+}
 
-#[derive(PartialEq, Props)]
-pub struct MyProps {
+#[derive(Props)]
+pub struct MyProps<'a> {
     count: u32,
     live: bool,
     name: &'static str,
+    children: Element<'a>,
 }
 
-pub static Example1: Component<MyProps> = |cx, MyProps { count, live, name }| {
+pub fn Example1(cx: Scope<MyProps>) -> Element {
+    let MyProps { count, live, name } = cx.props;
     cx.render(rsx! {
         div {
             h1 { "Hello, {name}"}
             h3 {"Are we alive? {live}"}
             p {"Count is {count}"}
-            { cx.children() }
+            &cx.props.children
         }
     })
-};
+}

+ 0 - 10
examples/core_reference/statemanagement.rs

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

+ 2 - 2
examples/core_reference/suspense.rs

@@ -14,7 +14,7 @@ struct DogApi {
 }
 const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
 
-pub static Example: Component = |cx| {
+pub fn Example(cx: Scope) -> Element {
     let doggo = use_suspense(
         cx,
         || surf::get(ENDPOINT).recv_json::<DogApi>(),
@@ -35,4 +35,4 @@ pub static Example: Component = |cx| {
             {doggo}
         }
     ))
-};
+}

+ 3 - 3
examples/core_reference/task.rs

@@ -42,19 +42,19 @@ pub static Example: Component = |cx| {
         div {
             h1 {"count is {count}"}
             button {
+                onclick: move |_| task.stop(),
                 "Stop counting"
-                onclick: move |_| task.stop()
             }
             button {
+                onclick: move |_| task.resume(),
                 "Start counting"
-                onclick: move |_| task.resume()
             }
             button {
-                "Switch counting direcion"
                 onclick: move |_| {
                     direction *= -1;
                     task.restart();
                 }
+                "Switch counting direcion"
             }
         }
     })

+ 0 - 9
examples/core_reference/testing.rs

@@ -1,9 +0,0 @@
-use dioxus::prelude::*;
-
-pub static Example: Component = |cx| {
-    cx.render(rsx! {
-        div {
-
-        }
-    })
-};

+ 0 - 25
examples/core_reference/tostring.rs

@@ -1,25 +0,0 @@
-use dioxus::prelude::*;
-use dioxus::ssr;
-
-pub static Example: Component = |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();
-        ssr::render_vdom(&dom)
-    });
-
-    cx.render(rsx! {
-        div { "{as_string}" }
-    })
-};
-
-static SomeApp: Component = |cx| {
-    cx.render(rsx! {
-        div { style: {background_color: "blue"}
-            h1 {"Some amazing app or component"}
-            p {"Things are great"}
-        }
-    })
-};

+ 3 - 3
examples/coroutine.rs

@@ -22,12 +22,12 @@
 //! the coroutine was initiated. `use_state` always returns the same setter, so you don't need to worry about
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
 use dioxus::prelude::*;
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let p1 = use_state(&cx, || 0);
     let p2 = use_state(&cx, || 0);
 
@@ -53,7 +53,7 @@ static App: Component = |cx| {
             Horsey { pos: *p2, "horsey 2" }
         }
     })
-};
+}
 
 #[derive(Props)]
 struct HorseyProps<'a> {

+ 1 - 2
examples/crm.rs

@@ -20,12 +20,11 @@ pub struct Client {
 }
 
 fn app(cx: Scope) -> Element {
-    let clients = use_ref(&cx, || vec![] as Vec<Client>);
-
     let scene = use_state(&cx, || Scene::ClientsList);
     let firstname = use_state(&cx, String::new);
     let lastname = use_state(&cx, String::new);
     let description = use_state(&cx, String::new);
+    let clients = use_ref(&cx, || vec![] as Vec<Client>);
 
     cx.render(rsx!(
         body { 

+ 0 - 5
examples/desktop/core.rs

@@ -1,5 +0,0 @@
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_html as dioxus_elements;
-
-fn main() {}

+ 0 - 109
examples/desktop/crm.rs

@@ -1,109 +0,0 @@
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-fn main() {
-    dioxus_desktop::launch(App)
-}
-
-enum Scene {
-    ClientsList,
-    NewClientForm,
-    Settings,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct Client {
-    pub first_name: String,
-    pub last_name: String,
-    pub description: String,
-}
-
-static App: Component = |cx| {
-    let mut scene = use_state(&cx, || Scene::ClientsList);
-    let clients = use_ref(&cx, || vec![] as Vec<Client>);
-
-    let mut firstname = use_state(&cx, String::new);
-    let mut lastname = use_state(&cx, String::new);
-    let mut description = use_state(&cx, String::new);
-
-    let scene = match scene.get() {
-        Scene::ClientsList => {
-            rsx!(cx, div { class: "crm"
-                h2 { "List of clients" margin_bottom: "10px" }
-                div { class: "clients" margin_left: "10px"
-                    {clients.read().iter().map(|client| rsx!(
-                        div { class: "client" style: "margin-bottom: 50px"
-                            p { "First Name: {client.first_name}" }
-                            p { "Last Name: {client.last_name}" }
-                            p {"Description: {client.description}"}
-                        })
-                    )}
-                }
-                button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
-                button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" }
-            })
-        }
-        Scene::NewClientForm => {
-            let add_new = move |_| {
-                clients.write().push(Client {
-                    description: (*description).clone(),
-                    first_name: (*firstname).clone(),
-                    last_name: (*lastname).clone(),
-                });
-                description.set(String::new());
-                firstname.set(String::new());
-                lastname.set(String::new());
-            };
-            rsx!(cx, div { class: "crm"
-                h2 {"Add new client" margin_bottom: "10px" }
-                form { class: "pure-form"
-                    input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
-                        oninput: move |evt| firstname.set(evt.value)
-                    }
-                    input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
-                        oninput: move |evt| lastname.set(evt.value)
-                    }
-                    textarea { class: "new-client description" placeholder: "Description" value: "{description}"
-                        oninput: move |evt| description.set(evt.value)
-                    }
-                }
-                button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
-                button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" }
-            })
-        }
-        Scene::Settings => {
-            rsx!(cx, div {
-                h2 { "Settings" margin_bottom: "10px" }
-                button {
-                    background: "rgb(202, 60, 60)"
-                    class: "pure-button pure-button-primary"
-                    onclick: move |_| {
-                        clients.write().clear();
-                        scene.set(Scene::ClientsList);
-                    },
-                    "Remove all clients"
-                }
-                button {
-                    class: "pure-button pure-button-primary"
-                    onclick: move |_| scene.set(Scene::ClientsList),
-                    "Go Back"
-                }
-            })
-        }
-    };
-
-    rsx!(cx, body {
-        link {
-            rel: "stylesheet"
-            href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
-            integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
-            crossorigin: "anonymous"
-        }
-        margin_left: "35%"
-        h1 {"Dioxus CRM Example"}
-        {scene}
-    })
-};

+ 0 - 18
examples/desktop/demo.rs

@@ -1,18 +0,0 @@
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-
-use dioxus_html as dioxus_elements;
-
-fn main() {
-    dioxus_desktop::launch(App);
-}
-
-static App: Component = |cx| {
-    cx.render(rsx!(
-        div {
-            "hello world!"
-        }
-        (0..10).map(|f| rsx!( div {"abc {f}"}))
-    ))
-};

+ 0 - 379
examples/desktop/todomvc.css

@@ -1,379 +0,0 @@
-html,
-body {
-    margin: 0;
-    padding: 0;
-}
-
-button {
-    margin: 0;
-    padding: 0;
-    border: 0;
-    background: none;
-    font-size: 100%;
-    vertical-align: baseline;
-    font-family: inherit;
-    font-weight: inherit;
-    color: inherit;
-    -webkit-appearance: none;
-    appearance: none;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-}
-
-body {
-    font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
-    line-height: 1.4em;
-    background: #f5f5f5;
-    color: #4d4d4d;
-    min-width: 230px;
-    max-width: 550px;
-    margin: 0 auto;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-    font-weight: 300;
-}
-
-:focus {
-    outline: 0;
-}
-
-.hidden {
-    display: none;
-}
-
-.todoapp {
-    background: #fff;
-    margin: 130px 0 40px 0;
-    position: relative;
-    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
-        0 25px 50px 0 rgba(0, 0, 0, 0.1);
-}
-
-.todoapp input::-webkit-input-placeholder {
-    font-style: italic;
-    font-weight: 300;
-    color: #e6e6e6;
-}
-
-.todoapp input::-moz-placeholder {
-    font-style: italic;
-    font-weight: 300;
-    color: #e6e6e6;
-}
-
-.todoapp input::input-placeholder {
-    font-style: italic;
-    font-weight: 300;
-    color: #e6e6e6;
-}
-
-.todoapp h1 {
-    position: absolute;
-    top: -155px;
-    width: 100%;
-    font-size: 100px;
-    font-weight: 100;
-    text-align: center;
-    color: rgba(175, 47, 47, 0.15);
-    -webkit-text-rendering: optimizeLegibility;
-    -moz-text-rendering: optimizeLegibility;
-    text-rendering: optimizeLegibility;
-}
-
-.new-todo,
-.edit {
-    position: relative;
-    margin: 0;
-    width: 100%;
-    font-size: 24px;
-    font-family: inherit;
-    font-weight: inherit;
-    line-height: 1.4em;
-    border: 0;
-    color: inherit;
-    padding: 6px;
-    border: 1px solid #999;
-    box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-    box-sizing: border-box;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-}
-
-.new-todo {
-    padding: 16px 16px 16px 60px;
-    border: none;
-    background: rgba(0, 0, 0, 0.003);
-    box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
-}
-
-.main {
-    position: relative;
-    z-index: 2;
-    border-top: 1px solid #e6e6e6;
-}
-
-.toggle-all {
-    text-align: center;
-    border: none;
-    /* Mobile Safari */
-    opacity: 0;
-    position: absolute;
-}
-
-.toggle-all+label {
-    width: 60px;
-    height: 34px;
-    font-size: 0;
-    position: absolute;
-    top: -52px;
-    left: -13px;
-    -webkit-transform: rotate(90deg);
-    transform: rotate(90deg);
-}
-
-.toggle-all+label:before {
-    content: '❯';
-    font-size: 22px;
-    color: #e6e6e6;
-    padding: 10px 27px 10px 27px;
-}
-
-.toggle-all:checked+label:before {
-    color: #737373;
-}
-
-.todo-list {
-    margin: 0;
-    padding: 0;
-    list-style: none;
-}
-
-.todo-list li {
-    position: relative;
-    font-size: 24px;
-    border-bottom: 1px solid #ededed;
-}
-
-.todo-list li:last-child {
-    border-bottom: none;
-}
-
-.todo-list li.editing {
-    border-bottom: none;
-    padding: 0;
-}
-
-.todo-list li.editing .edit {
-    display: block;
-    width: 506px;
-    padding: 12px 16px;
-    margin: 0 0 0 43px;
-}
-
-.todo-list li.editing .view {
-    display: none;
-}
-
-.todo-list li .toggle {
-    text-align: center;
-    width: 40px;
-    /* auto, since non-WebKit browsers doesn't support input styling */
-    height: auto;
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    margin: auto 0;
-    border: none;
-    /* Mobile Safari */
-    -webkit-appearance: none;
-    appearance: none;
-}
-
-.todo-list li .toggle {
-    opacity: 0;
-}
-
-.todo-list li .toggle+label {
-    /*
-		Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
-		IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
-	*/
-    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
-    background-repeat: no-repeat;
-    background-position: center left;
-}
-
-.todo-list li .toggle:checked+label {
-    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
-}
-
-.todo-list li label {
-    word-break: break-all;
-    padding: 15px 15px 15px 60px;
-    display: block;
-    line-height: 1.2;
-    transition: color 0.4s;
-}
-
-.todo-list li.completed label {
-    color: #d9d9d9;
-    text-decoration: line-through;
-}
-
-.todo-list li .destroy {
-    display: none;
-    position: absolute;
-    top: 0;
-    right: 10px;
-    bottom: 0;
-    width: 40px;
-    height: 40px;
-    margin: auto 0;
-    font-size: 30px;
-    color: #cc9a9a;
-    margin-bottom: 11px;
-    transition: color 0.2s ease-out;
-}
-
-.todo-list li .destroy:hover {
-    color: #af5b5e;
-}
-
-.todo-list li .destroy:after {
-    content: '×';
-}
-
-.todo-list li:hover .destroy {
-    display: block;
-}
-
-.todo-list li .edit {
-    display: none;
-}
-
-.todo-list li.editing:last-child {
-    margin-bottom: -1px;
-}
-
-.footer {
-    color: #777;
-    padding: 10px 15px;
-    height: 20px;
-    text-align: center;
-    border-top: 1px solid #e6e6e6;
-}
-
-.footer:before {
-    content: '';
-    position: absolute;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    height: 50px;
-    overflow: hidden;
-    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
-        0 8px 0 -3px #f6f6f6,
-        0 9px 1px -3px rgba(0, 0, 0, 0.2),
-        0 16px 0 -6px #f6f6f6,
-        0 17px 2px -6px rgba(0, 0, 0, 0.2);
-}
-
-.todo-count {
-    float: left;
-    text-align: left;
-}
-
-.todo-count strong {
-    font-weight: 300;
-}
-
-.filters {
-    margin: 0;
-    padding: 0;
-    list-style: none;
-    position: absolute;
-    right: 0;
-    left: 0;
-}
-
-.filters li {
-    display: inline;
-}
-
-.filters li a {
-    color: inherit;
-    margin: 3px;
-    padding: 3px 7px;
-    text-decoration: none;
-    border: 1px solid transparent;
-    border-radius: 3px;
-}
-
-.filters li a:hover {
-    border-color: rgba(175, 47, 47, 0.1);
-}
-
-.filters li a.selected {
-    border-color: rgba(175, 47, 47, 0.2);
-}
-
-.clear-completed,
-html .clear-completed:active {
-    float: right;
-    position: relative;
-    line-height: 20px;
-    text-decoration: none;
-    cursor: pointer;
-}
-
-.clear-completed:hover {
-    text-decoration: underline;
-}
-
-.info {
-    margin: 65px auto 0;
-    color: #bfbfbf;
-    font-size: 10px;
-    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-    text-align: center;
-}
-
-.info p {
-    line-height: 1;
-}
-
-.info a {
-    color: inherit;
-    text-decoration: none;
-    font-weight: 400;
-}
-
-.info a:hover {
-    text-decoration: underline;
-}
-
-/*
-	Hack to remove background from Mobile Safari.
-	Can't use it globally since it destroys checkboxes in Firefox
-*/
-@media screen and (-webkit-min-device-pixel-ratio:0) {
-
-    .toggle-all,
-    .todo-list li .toggle {
-        background: none;
-    }
-
-    .todo-list li .toggle {
-        height: 40px;
-    }
-}
-
-@media (max-width: 430px) {
-    .footer {
-        height: 50px;
-    }
-
-    .filters {
-        bottom: 10px;
-    }
-}

+ 0 - 170
examples/desktop/todomvc.rs

@@ -1,170 +0,0 @@
-#![allow(non_upper_case_globals, non_snake_case)]
-
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-use std::collections::HashMap;
-
-fn main() {
-    dioxus_desktop::launch(App)
-}
-
-#[derive(PartialEq)]
-pub enum FilterState {
-    All,
-    Active,
-    Completed,
-}
-
-#[derive(Debug, PartialEq)]
-pub struct TodoItem {
-    pub id: u32,
-    pub checked: bool,
-    pub contents: String,
-}
-pub type Todos = HashMap<u32, TodoItem>;
-
-pub static App: Component = |cx| {
-    // Share our TodoList to the todos themselves
-    use_provide_state(cx, Todos::new);
-
-    // Save state for the draft, filter
-    let draft = use_state(&cx, || "".to_string());
-    let filter = use_state(&cx, || FilterState::All);
-    let mut todo_id = use_state(&cx, || 0);
-
-    // Consume the todos
-    let todos = use_shared_state::<Todos>(cx)?;
-
-    // Filter the todos based on the filter state
-    let mut filtered_todos = todos
-        .read()
-        .iter()
-        .filter(|(_, item)| match *filter {
-            FilterState::All => true,
-            FilterState::Active => !item.checked,
-            FilterState::Completed => item.checked,
-        })
-        .map(|f| *f.0)
-        .collect::<Vec<_>>();
-    filtered_todos.sort_unstable();
-
-    // Define the actions to manage the todolist
-    let mut submit_todo = move || {
-        if !draft.is_empty() {
-            todos.write().insert(
-                *todo_id,
-                TodoItem {
-                    id: *todo_id,
-                    checked: false,
-                    contents: draft.get().clone(),
-                },
-            );
-            todo_id += 1;
-            draft.set("".to_string());
-        }
-    };
-    let clear_completed = move || {
-        todos.write().retain(|_, todo| todo.checked == false);
-    };
-
-    // Some assists in actually rendering the content
-    let show_clear_completed = todos.read().values().any(|todo| todo.checked);
-    let items_left = filtered_todos.len();
-    let item_text = match items_left {
-        1 => "item",
-        _ => "items",
-    };
-
-    cx.render(rsx!{
-        section { class: "todoapp"
-            style { {[include_str!("./todomvc.css")]} }
-            div {
-                header { class: "header"
-                    h1 {"todos"}
-                    input {
-                        class: "new-todo"
-                        placeholder: "What needs to be done?"
-                        value: "{draft}"
-                        autofocus: "true"
-                        oninput: move |evt| draft.set(evt.value)
-                        onkeydown: move |evt| {
-                            if evt.key == "Enter" {
-                                submit_todo();
-                            }
-                        }
-                    }
-                }
-                ul { class: "todo-list",
-                    filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id }))
-                }
-                (!todos.read().is_empty()).then(|| rsx!(
-                    footer { class: "footer",
-                        span { class: "todo-count" strong {"{items_left} "} span {"{item_text} left"} }
-                        ul { class: "filters"
-                            li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
-                            li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
-                            li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
-                        }
-                        show_clear_completed.then(|| rsx!(
-                            button { class: "clear-completed", onclick: move |_| clear_completed(),
-                                "Clear 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 {
-    id: u32,
-}
-
-pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
-    let todos = use_shared_state::<Todos>(cx)?;
-
-    let _todos = todos.read();
-    let todo = _todos.get(&cx.props.id)?;
-
-    let is_editing = use_state(&cx, || false);
-    let completed = if todo.checked { "completed" } else { "" };
-
-    cx.render(rsx!{
-        li { class: "{completed}"
-            div { class: "view"
-                input { class: "toggle" r#type: "checkbox" id: "cbg-{todo.id}" checked: "{todo.checked}"
-                    onchange: move |evt| {
-                        if let Some(todo) = todos.write().get_mut(&cx.props.id) { 
-                            todo.checked = evt.value.parse().unwrap()
-                        }
-                    }
-                }
-
-                label { r#for: "cbg-{todo.id}" pointer_events: "none"
-                    "{todo.contents}"
-                }
-
-               {is_editing.then(|| rsx!{
-                    input { value: "{todo.contents}"
-                        oninput: move |evt| {
-                            if let Some(todo) = todos.write().get_mut(&cx.props.id) { 
-                                todo.contents = evt.value
-                            }
-                        },
-                    }
-                })}
-            }
-        }
-    })
-}

+ 16 - 8
examples/framework_benchmark.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use rand::prelude::*;
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
 #[derive(Clone, PartialEq)]
@@ -29,7 +29,7 @@ impl Label {
     }
 }
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let items = use_ref(&cx, || vec![]);
     let selected = use_state(&cx, || None);
 
@@ -64,7 +64,7 @@ static App: Component = |cx| {
             }
             table {
                 tbody {
-                    {items.read().iter().enumerate().map(|(id, item)| {
+                    items.read().iter().enumerate().map(|(id, item)| {
                         let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
                         rsx!(tr { class: "{is_in_danger}",
                             td { class:"col-md-1" }
@@ -79,13 +79,13 @@ static App: Component = |cx| {
                             }
                             td { class: "col-md-6" }
                         })
-                    })}
+                    })
                 }
              }
             span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" }
         }
     })
-};
+}
 
 #[derive(Props)]
 struct ActionButtonProps<'a> {
@@ -95,9 +95,17 @@ struct ActionButtonProps<'a> {
 }
 
 fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
-    rsx!(cx, 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)(),
-            "{cx.props.name}"
+    cx.render(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)(),
+
+                "{cx.props.name}"
+            }
         }
     })
 }

+ 5 - 4
examples/hydration.rs

@@ -13,12 +13,13 @@ use dioxus::prelude::*;
 use dioxus::ssr;
 
 fn main() {
-    let vdom = VirtualDom::new(App);
+    let vdom = VirtualDom::new(app);
     let content = ssr::render_vdom_cfg(&vdom, |f| f.pre_render(true));
-    dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content));
+
+    dioxus::desktop::launch_cfg(app, |c| c.with_prerendered(content));
 }
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let val = use_state(&cx, || 0);
 
     cx.render(rsx! {
@@ -30,4 +31,4 @@ static App: Component = |cx| {
             }
         }
     })
-};
+}

+ 3 - 3
examples/inputs.rs

@@ -7,7 +7,7 @@ use std::sync::Arc;
 use dioxus::{events::FormEvent, prelude::*};
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
 const FIELDS: &[(&str, &str)] = &[
@@ -37,7 +37,7 @@ const FIELDS: &[(&str, &str)] = &[
     ("week", ""),  // degrades to text most of the time
 ];
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     cx.render(rsx! {
         div { margin_left: "30px",
 
@@ -147,4 +147,4 @@ static App: Component = |cx| {
 
         }
     })
-};
+}

+ 1 - 1
examples/manual_edits.rs

@@ -33,7 +33,7 @@ fn main() {
         AppendChildren { many: 1 },
     ];
 
-    let app: Component = |cx| rsx!(cx, div { "some app" });
+    let app: Component = |cx| cx.render(rsx!(div { "some app" }));
 
     dioxus_desktop::run(app, (), |c| c.with_edits(edits));
 }

+ 27 - 24
examples/pattern_model.rs

@@ -21,7 +21,6 @@ use dioxus::desktop::wry::application::dpi::LogicalSize;
 use dioxus::events::*;
 use dioxus::prelude::*;
 
-const STYLE: &str = include_str!("./assets/calculator.css");
 fn main() {
     env_logger::init();
     dioxus::desktop::launch_cfg(app, |cfg| {
@@ -40,32 +39,36 @@ fn app(cx: Scope) -> Element {
     let clear_text = if clear_display { "C" } else { "AC" };
     let formatted = state.read().formatted_display();
 
-    rsx!(cx, div { id: "wrapper",
-        div { class: "app", style { "{STYLE}" }
-            div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
-                div { class: "calculator-display", "{formatted}"}
-                div { class: "calculator-keypad",
-                    div { class: "input-keys",
-                        div { class: "function-keys",
-                            CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(), "{clear_text}" }
-                            CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±"}
-                            CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%"}
+    cx.render(rsx!{
+        div { id: "wrapper",
+            div { 
+                class: "app", 
+                style { [include_str!("./assets/calculator.css")] }
+                div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
+                    div { class: "calculator-display", "{formatted}"}
+                    div { class: "calculator-keypad",
+                        div { class: "input-keys",
+                            div { class: "function-keys",
+                                CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(), "{clear_text}" }
+                                CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±"}
+                                CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%"}
+                            }
+                            div { class: "digit-keys",
+                                CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" }
+                                CalculatorKey { name: "key-dot", onclick: move |_|  state.write().input_dot(), "●" }
+                                (1..10).map(move |k| rsx!{
+                                    CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_|  state.write().input_digit(k), "{k}" }
+                                })
+                            }
                         }
-                        div { class: "digit-keys",
-                            CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" }
-                            CalculatorKey { name: "key-dot", onclick: move |_|  state.write().input_dot(), "●" }
-                            (1..10).map(move |k| rsx!{
-                                CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_|  state.write().input_digit(k), "{k}" }
-                            })
+                        div { class: "operator-keys",
+                            CalculatorKey { name:"key-divide", onclick: move |_| state.write().set_operator(Operator::Div), "÷" }
+                            CalculatorKey { name:"key-multiply", onclick: move |_| state.write().set_operator(Operator::Mul), "×" }
+                            CalculatorKey { name:"key-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" }
+                            CalculatorKey { name:"key-add", onclick: move |_| state.write().set_operator(Operator::Add), "+" }
+                            CalculatorKey { name:"key-equals", onclick: move |_| state.write().perform_operation(), "=" }
                         }
                     }
-                    div { class: "operator-keys",
-                        CalculatorKey { name:"key-divide", onclick: move |_| state.write().set_operator(Operator::Div), "÷" }
-                        CalculatorKey { name:"key-multiply", onclick: move |_| state.write().set_operator(Operator::Mul), "×" }
-                        CalculatorKey { name:"key-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" }
-                        CalculatorKey { name:"key-add", onclick: move |_| state.write().set_operator(Operator::Add), "+" }
-                        CalculatorKey { name:"key-equals", onclick: move |_| state.write().perform_operation(), "=" }
-                    }
                 }
             }
         }

+ 3 - 3
examples/pattern_reducer.rs

@@ -8,10 +8,10 @@
 use dioxus::prelude::*;
 fn main() {
     env_logger::init();
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
-pub static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let state = use_state(&cx, PlayerState::new);
 
     cx.render(rsx!(
@@ -26,7 +26,7 @@ pub static App: Component = |cx| {
             }
         }
     ))
-};
+}
 
 enum PlayerAction {
     Pause,

+ 3 - 3
examples/readme.rs

@@ -5,10 +5,10 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let mut count = use_state(&cx, || 0);
 
     cx.render(rsx! {
@@ -18,4 +18,4 @@ static App: Component = |cx| {
             button { onclick: move |_| count -= 1, "Down low!" }
         }
     })
-};
+}

+ 2 - 2
examples/router.rs

@@ -23,7 +23,7 @@ pub enum Route {
     NotFound,
 }
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let route = use_router(&cx, Route::parse);
 
     cx.render(rsx! {
@@ -42,7 +42,7 @@ static App: Component = |cx| {
         }
         footer {}
     })
-};
+}
 
 impl Route {
     // Generate the appropriate route from the "tail" end of the URL

+ 3 - 0
examples/rsx_compile_fail.rs

@@ -1,3 +1,6 @@
+//! This example just flexes the ability to use arbitrary expressions within RSX.
+//! It also proves that lifetimes work properly, especially when used with use_ref
+
 use dioxus::prelude::*;
 
 fn main() {

+ 9 - 5
examples/rsx_usage.rs

@@ -39,7 +39,7 @@
 //! - Allow top-level fragments
 //!
 fn main() {
-    dioxus::desktop::launch(EXAMPLE);
+    dioxus::desktop::launch(app);
 }
 
 /// When trying to return "nothing" to Dioxus, you'll need to specify the type parameter or Rust will be sad.
@@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
 use baller::Baller;
 use dioxus::prelude::*;
 
-pub static EXAMPLE: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let formatting = "formatting!";
     let formatting_tuple = ("a", "b");
     let lazy_fmt = format_args!("lazily formatted text");
@@ -192,10 +192,12 @@ pub static EXAMPLE: Component = |cx| {
             [helper(&cx, "hello world!")]
         }
     })
-};
+}
 
 fn helper<'a>(cx: &'a ScopeState, text: &str) -> Element<'a> {
-    rsx!(cx, p { "{text}" })
+    cx.render(rsx! {
+        p { "{text}" }
+    })
 }
 
 mod baller {
@@ -227,5 +229,7 @@ pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element {
 
 #[inline_props]
 fn with_inline<'a>(cx: Scope<'a>, text: &'a str) -> Element {
-    rsx!(cx, p { "{text}" })
+    cx.render(rsx! {
+        p { "{text}" }
+    })
 }

+ 3 - 3
examples/ssr.rs

@@ -2,16 +2,16 @@ use dioxus::prelude::*;
 use dioxus::ssr;
 
 fn main() {
-    let mut vdom = VirtualDom::new(APP);
+    let mut vdom = VirtualDom::new(app);
     let _ = vdom.rebuild();
     println!("{}", ssr::render_vdom(&vdom));
 }
 
-static APP: Component = |cx| {
+fn app(cx: Scope) -> Element {
     cx.render(rsx!(
         div {
             h1 { "Title" }
             p { "Body" }
         }
     ))
-};
+}

+ 5 - 4
examples/ssr/basic.rs

@@ -3,8 +3,9 @@ use dioxus_core as dioxus;
 use dioxus_core::prelude::*;
 use dioxus_core_macro::*;
 use dioxus_html as dioxus_elements;
+
 fn main() {
-    let mut dom = VirtualDom::new(App);
+    let mut dom = VirtualDom::new(app);
     dom.rebuild();
     println!(
         "{}",
@@ -12,14 +13,14 @@ fn main() {
     )
 }
 
-pub static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     cx.render(rsx!(
         div {
             class: "overflow-hidden"
             ul {
-                {(0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})}
+                (0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})
             }
             "hello world!"
         }
     ))
-};
+}

+ 0 - 10
examples/ssr/hydration.rs

@@ -1,10 +0,0 @@
-//! Example: realworld usage of hydration
-//!
-//!
-use dioxus::virtual_dom::VirtualDom;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_hooks::use_state;
-use dioxus_html as dioxus_elements;
-
-fn main() {}

+ 0 - 298
examples/ssr/template.html

@@ -1,298 +0,0 @@
-<!-- a js-only interpreter for the dioxus patch stream :) -->
-<!DOCTYPE html>
-<html>
-
-<head>
-    <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
-    <meta charset="UTF-8" />
-    <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
-</head>
-
-<body>
-    <div>hello dioxus</div>
-</body>
-<script>
-    class Interpreter {
-        constructor(root) {
-            this.stack = [root];
-        }
-
-        top() {
-            return this.stack[this.stack.length - 1];
-        }
-
-        pop() {
-            return this.stack.pop();
-        }
-    }
-
-    class OPTABLE {
-        // 0
-        SetText(edit, interp) {
-            interp.top(interp.stack).textContent = edit.text;
-        }
-        // 1
-        RemoveSelfAndNextSiblings(edit) {
-            const node = interp.pop();
-            let sibling = node.nextSibling;
-            while (sibling) {
-                const temp = sibling.nextSibling;
-                sibling.remove();
-                sibling = temp;
-            }
-            node.remove();
-        }
-
-        // 2
-        ReplaceWith(edit, interp) {
-            const newNode = interp.pop();
-            const oldNode = interp.pop();
-            oldNode.replaceWith(newNode);
-            interp.stack.push(newNode);
-        }
-
-        // 3
-        SetAttribute(edit, interp) {
-            const name = edit.name;
-            const value = edit.value;
-            const node = interp.top(interp.stack);
-            node.setAttribute(name, value);
-
-            // Some attributes are "volatile" and don't work through `setAttribute`.
-            if ((name === "value", interp)) {
-                node.value = value;
-            }
-            if ((name === "checked", interp)) {
-                node.checked = true;
-            }
-            if ((name === "selected", interp)) {
-                node.selected = true;
-            }
-        }
-
-        // 4
-        RemoveAttribute(edit, interp) {
-            const name = edit.name;
-            const node = interp.top(interp.stack);
-            node.removeAttribute(name);
-
-            // Some attributes are "volatile" and don't work through `removeAttribute`.
-            if ((name === "value", interp)) {
-                node.value = null;
-            }
-            if ((name === "checked", interp)) {
-                node.checked = false;
-            }
-            if ((name === "selected", interp)) {
-                node.selected = false;
-            }
-        }
-
-        // 5
-        PushReverseChild(edit, interp) {
-            const n = edit.n;
-            const parent = interp.top(interp.stack);
-            const children = parent.childNodes;
-            const child = children[children.length - n - 1];
-            interp.stack.push(child);
-        }
-
-        // 6
-        PopPushChild(edit, interp) {
-            const n = edit.n;
-            interp.pop();
-            const parent = interp.top(interp.stack);
-            const children = parent.childNodes;
-            const child = children[n];
-            interp.stack.push(child);
-        }
-
-        // 7
-        Pop(edit, interp) {
-            interp.pop();
-        }
-
-        // 8
-        AppendChild(edit, interp) {
-            console.log(interp.stack);
-            const child = interp.pop();
-            console.log(interp.stack);
-            interp.top().appendChild(child);
-        }
-
-        // 9
-        CreateTextNode(edit, interp) {
-            // interp.stack.push(document.createTextNode("asd"));
-            console.log(interp.stack);
-
-            interp.stack.push(document.createTextNode(edit.text));
-            console.log(interp.stack);
-        }
-
-        // 10
-        CreateElement(edit, interp) {
-            const tagName = edit.tag_name;
-            interp.stack.push(document.createElement(tagName));
-        }
-
-        // 11
-        NewEventListener(edit, interp) {
-            // todo!
-            const eventId = mem32[i++];
-            const eventType = interp.getCachedString(eventId);
-            const a = mem32[i++];
-            const b = mem32[i++];
-            const el = interp.top(interp.stack);
-            el.addEventListener(eventType, interp.eventHandler);
-            el[`dodrio-a-${eventType}`] = a;
-            el[`dodrio-b-${eventType}`] = b;
-        }
-
-        // 12
-        UpdateEventListener(edit, interp) {
-            // todo!
-            const eventId = mem32[i++];
-            const eventType = interp.getCachedString(eventId);
-            const el = interp.top(interp.stack);
-            el[`dodrio-a-${eventType}`] = mem32[i++];
-            el[`dodrio-b-${eventType}`] = mem32[i++];
-        }
-
-        // 13
-        RemoveEventListener(edit, interp) {
-            // todo!
-            const eventId = mem32[i++];
-            const eventType = interp.getCachedString(eventId);
-            const el = interp.top(interp.stack);
-            el.removeEventListener(eventType, interp.eventHandler);
-        }
-
-        // 14
-        AddCachedString(edit, interp) {
-            // todo!
-            const pointer = mem32[i++];
-            const length = mem32[i++];
-            const id = mem32[i++];
-            const str = string(mem8, pointer, length);
-            interp.addCachedString(str, id);
-        }
-
-        // 15
-        DropCachedString(edit, interp) {
-            // todo!
-            const id = mem32[i++];
-            interp.dropCachedString(id);
-        }
-
-        // 16
-        CreateElementNS(edit, interp) {
-            //   const tagNameId = mem32[i++];
-            //   const tagName = interp.getCachedString(tagNameId);
-            //   const nsId = mem32[i++];
-            //   const ns = interp.getCachedString(nsId);
-            interp.stack.push(document.createElementNS(edit.ns, edit.tag_name));
-        }
-
-        // 17
-        SaveChildrenToTemporaries(edit, interp) {
-            //   let temp = mem32[i++];
-            //   const start = mem32[i++];
-            //   const end = mem32[i++];
-            let temp = edit.temp;
-            const start = edit.start;
-            const end = edit.end;
-            const parent = interp.top(interp.stack);
-            const children = parent.childNodes;
-            for (let i = start; i < end; i++, interp) {
-                interp.temporaries[temp++] = children[i];
-            }
-        }
-
-        // 18
-        PushChild(edit, interp) {
-            const parent = interp.top(interp.stack);
-            //   const n = mem32[i++];
-            const n = edit.n;
-            const child = parent.childNodes[n];
-            interp.stack.push(child);
-        }
-
-        // 19
-        PushTemporary(edit, interp) {
-            //   const temp = mem32[i++];
-            const temp = edit.temp;
-            interp.stack.push(interp.temporaries[temp]);
-        }
-
-        // 20
-        InsertBefore(edit, interp) {
-            const before = interp.pop();
-            const after = interp.pop();
-            after.parentNode.insertBefore(before, after);
-            interp.stack.push(before);
-        }
-
-        // 21
-        PopPushReverseChild(edit, interp) {
-            //   const n = mem32[i++];
-            const n = edit.n;
-            interp.pop();
-            const parent = interp.top(interp.stack);
-            const children = parent.childNodes;
-            const child = children[children.length - n - 1];
-            interp.stack.push(child);
-        }
-
-        // 22
-        RemoveChild(edit, interp) {
-            //   const n = mem32[i++];
-            const n = edit.n;
-            const parent = interp.top(interp.stack);
-            const child = parent.childNodes[n];
-            child.remove();
-        }
-
-        // 23
-        SetClass(edit, interp) {
-            //   const classId = mem32[i++];
-            const className = edit.class_name;
-            interp.top(interp.stack).className = className;
-        }
-
-        // 24
-        SaveTemplate(edit, interp) {
-            const id = mem32[i++];
-            const template = interp.top(interp.stack);
-            interp.saveTemplate(id, template.cloneNode(true));
-        }
-
-        // 25
-        PushTemplate(edit, interp) {
-            const id = mem32[i++];
-            const template = interp.getTemplate(id);
-            interp.stack.push(template.cloneNode(true));
-        }
-
-        NewListener(edit, interp) {}
-    }
-
-    const op_table = new OPTABLE();
-    const interpreter = new Interpreter(window.document.body);
-
-    function EditListReceived(rawEditList) {
-        let editList = JSON.parse(rawEditList);
-        editList.forEach(function (edit, index) {
-            op_table[edit.type](edit, interpreter);
-        });
-    }
-
-    let socket = new WebSocket("ws://127.0.0.1:8080/session/itsworkinggg");
-    socket.onmessage = function(event) {
-        console.log(event.data);
-        EditListReceived(event.data);
-    }
-    // external.invoke("initiate");
-
-</script>
-
-</html>

+ 0 - 61
examples/ssr/tide.rs

@@ -1,61 +0,0 @@
-//!
-//!
-//!
-use dioxus::virtual_dom::VirtualDom;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::use_state;
-use dioxus_html as dioxus_elements;
-
-fn main() {}
-// use tide::{Request, Response};
-
-// #[async_std::main]
-// async fn main() -> Result<(), std::io::Error> {
-//     let mut app = tide::new();
-
-//     app.at("/:name").get(|req: Request<()>| async move {
-//         let initial_name: String = req
-//             .param("name")
-//             .map(|f| f.parse().unwrap_or("...?".to_string()))
-//             .unwrap_or("...?".to_string());
-
-//         let mut dom = VirtualDom::new_with_props(Example, ExampleProps { initial_name });
-
-//         dom.rebuild();
-
-//         Ok(Response::builder(200)
-//             .body(format!("{}", dioxus_ssr::render_vdom(&dom)))
-//             .content_type(tide::http::mime::HTML)
-//             .build())
-//     });
-
-//     println!("Server available at [http://127.0.0.1:8080/bill]");
-//     app.listen("127.0.0.1:8080").await?;
-
-//     Ok(())
-// }
-
-// #[derive(PartialEq, Props)]
-// struct ExampleProps {
-//     initial_name: String,
-// }
-
-// static Example: Component<ExampleProps> = |cx| {
-//     let dispaly_name = use_state(&cx, move || props.initial_name.clone());
-
-//     cx.render(rsx! {
-//         div { class: "py-12 px-4 text-center w-full max-w-2xl mx-auto",
-//             span { class: "text-sm font-semibold"
-//                 "Dioxus Example: Jack and Jill"
-//             }
-//             h2 { class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
-//                 "Hello, {dispaly_name}"
-//             }
-//             ul {
-//                 {(0..10).map(|f| rsx!( li {"Element {f}"} ))}
-//             }
-//         }
-//     })
-// };

+ 14 - 12
examples/ssr/tofile.rs

@@ -24,7 +24,7 @@ fn main() {
     .unwrap();
 }
 
-pub static App: Component = |cx| {
+pub fn App(cx: Scope) -> Element {
     cx.render(rsx!(
         div { class: "overflow-hidden"
             link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -37,9 +37,9 @@ pub static App: Component = |cx| {
             Hero {}
         }
     ))
-};
+}
 
-pub static Header: Component = |cx| {
+pub fn Header(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font"
@@ -63,9 +63,9 @@ pub static Header: Component = |cx| {
             }
         }
     })
-};
+}
 
-pub static Hero: Component = |cx| {
+pub fn Hero(cx: Scope) -> Element {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -102,8 +102,9 @@ pub static Hero: Component = |cx| {
             }
         }
     })
-};
-pub static Entry: Component = |cx| {
+}
+
+pub fn Entry(cx: Scope) -> Element {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -114,9 +115,9 @@ pub static Entry: Component = |cx| {
             }
         }
     })
-};
+}
 
-pub static StacksIcon: Component = |cx| {
+pub fn StacksIcon(cx: Scope) -> Element {
     cx.render(rsx!(
         svg {
             xmlns: "http://www.w3.org/2000/svg"
@@ -130,8 +131,9 @@ pub static StacksIcon: Component = |cx| {
             path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
         }
     ))
-};
-pub static RightArrowIcon: Component = |cx| {
+}
+
+pub fn RightArrowIcon(cx: Scope) -> Element {
     cx.render(rsx!(
         svg {
             fill: "none"
@@ -144,4 +146,4 @@ pub static RightArrowIcon: Component = |cx| {
             path { d: "M5 12h14M12 5l7 7-7 7"}
         }
     ))
-};
+}

+ 12 - 12
examples/tailwind.rs

@@ -14,7 +14,7 @@ fn main() {
 
 const STYLE: &str = "body {overflow:hidden;}";
 
-pub static App: Component = |cx| {
+pub fn App(cx: Scope) -> Element {
     cx.render(rsx!(
         div { class: "overflow-hidden",
         style { "{STYLE}" }
@@ -28,9 +28,9 @@ pub static App: Component = |cx| {
             Hero {}
         }
     ))
-};
+}
 
-pub static Header: Component = |cx| {
+pub fn Header(cx: Scope) -> Element {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font",
@@ -54,9 +54,9 @@ pub static Header: Component = |cx| {
             }
         }
     })
-};
+}
 
-pub static Hero: Component = |cx| {
+pub fn Hero(cx: Scope) -> Element {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font",
@@ -93,8 +93,8 @@ pub static Hero: Component = |cx| {
             }
         }
     })
-};
-pub static Entry: Component = |cx| {
+}
+pub fn Entry(cx: Scope) -> Element {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font",
@@ -105,9 +105,9 @@ pub static Entry: Component = |cx| {
             }
         }
     })
-};
+}
 
-pub static StacksIcon: Component = |cx| {
+pub fn StacksIcon(cx: Scope) -> Element {
     cx.render(rsx!(
         svg {
             // xmlns: "http://www.w3.org/2000/svg"
@@ -121,8 +121,8 @@ pub static StacksIcon: Component = |cx| {
             path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
         }
     ))
-};
-pub static RightArrowIcon: Component = |cx| {
+}
+pub fn RightArrowIcon(cx: Scope) -> Element {
     cx.render(rsx!(
         svg {
             fill: "none",
@@ -135,4 +135,4 @@ pub static RightArrowIcon: Component = |cx| {
             path { d: "M5 12h14M12 5l7 7-7 7"}
         }
     ))
-};
+}

+ 7 - 6
examples/tasks.rs

@@ -12,13 +12,14 @@ fn main() {
 fn app(cx: Scope) -> Element {
     let count = use_state(&cx, || 0);
 
-    use_future(&cx, || {
-        for_async![count];
+    use_future(&cx, move || {
+        let count = UseState::for_async(&count);
+        // for_async![count];
         async move {
-            loop {
-                tokio::time::sleep(Duration::from_millis(1000)).await;
-                count += 1;
-            }
+            // loop {
+            //     tokio::time::sleep(Duration::from_millis(1000)).await;
+            //     count += 1;
+            // }
         }
     });
 

+ 49 - 45
examples/todomvc.rs

@@ -4,7 +4,7 @@ use im_rc::HashMap;
 use std::rc::Rc;
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
 #[derive(PartialEq)]
@@ -21,8 +21,7 @@ pub struct TodoItem {
     pub contents: String,
 }
 
-const STYLE: &str = include_str!("./assets/todomvc.css");
-const App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let draft = use_state(&cx, || "".to_string());
     let todos = use_state(&cx, || HashMap::<u32, Rc<TodoItem>>::new());
     let filter = use_state(&cx, || FilterState::All);
@@ -48,40 +47,43 @@ const App: Component = |cx| {
         _ => "items",
     };
 
-    rsx!(cx, 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.clone()),
-                }
-            }
-            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" }}
+    cx.render(rsx!(
+        div { id: "app",
+            style { [include_str!("./assets/todomvc.css")] }
+            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.clone()),
                     }
                 }
-            ))
-        }
-        footer { class: "info",
-            p {"Double-click to edit a todo"}
-            p { "Created by ", a {  href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
-            p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
+                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 {  href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
+                p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
+            }
         }
-    })
-};
+    ))
+}
 
 #[derive(PartialEq, Props)]
 pub struct TodoEntryProps {
@@ -93,18 +95,20 @@ pub fn TodoEntry(cx: Scope<TodoEntryProps>) -> Element {
     let contents = use_state(&cx, || String::from(""));
     let todo = &cx.props.todo;
 
-    rsx!(cx, li {
-        "{todo.id}"
-        input {
-            class: "toggle",
-            r#type: "checkbox",
-            "{todo.checked}"
-        }
-       {is_editing.then(|| rsx!{
+    cx.render(rsx! {
+        li {
+            "{todo.id}"
             input {
-                value: "{contents}",
-                oninput: move |evt| contents.set(evt.value.clone())
+                class: "toggle",
+                r#type: "checkbox",
+                "{todo.checked}"
             }
-        })}
+           is_editing.then(|| rsx!{
+                input {
+                    value: "{contents}",
+                    oninput: move |evt| contents.set(evt.value.clone())
+                }
+            })
+        }
     })
 }

+ 13 - 13
examples/weather_app.rs

@@ -7,22 +7,23 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
 const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather";
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     //
     let body = use_suspense(
-        cx,
+        &cx,
         || async {
-            let content = reqwest::get(ENDPOINT)
-                .await
-                .unwrap()
-                .json::<serde_json::Value>()
-                .await
-                .unwrap();
+            todo!()
+            // let content = reqwest::get(ENDPOINT)
+            //     .await
+            //     .unwrap()
+            //     .json::<serde_json::Value>()
+            //     .await
+            //     .unwrap();
         },
         |props| {
             //
@@ -35,13 +36,12 @@ static App: Component = |cx| {
             {body}
         }
     })
-};
+}
 
 #[derive(PartialEq, Props)]
 struct WeatherProps {}
 
-static WeatherDisplay: Component<WeatherProps> = |cx| {
-    //
+fn WeatherDisplay(cx: Scope<WeatherProps>) -> Element {
     cx.render(rsx!(
         div { class: "flex items-center justify-center flex-col",
             div { class: "flex items-center justify-center",
@@ -64,4 +64,4 @@ static WeatherDisplay: Component<WeatherProps> = |cx| {
             }
         }
     ))
-};
+}

+ 3 - 3
examples/web_tick.rs

@@ -13,10 +13,10 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let mut rng = SmallRng::from_entropy();
 
     cx.render(rsx! {
@@ -29,7 +29,7 @@ static App: Component = |cx| {
             }
         }
     })
-};
+}
 
 #[derive(PartialEq, Props)]
 struct RowProps {

+ 7 - 11
examples/xss_safety.rs

@@ -1,3 +1,7 @@
+//! XSS Safety
+//!
+//! This example proves that Dioxus is safe from XSS attacks.
+
 use dioxus::prelude::*;
 
 fn main() {
@@ -9,19 +13,11 @@ fn app(cx: Scope) -> Element {
 
     cx.render(rsx! {
         div {
-            "hello world!"
-
-            h1 { "{contents}" }
-
-            h3 { [contents.as_str()] }
-
+            h3 { "{contents}" }
             input {
                 value: "{contents}",
-                oninput: move |e| {
-                    contents.set(e.value.clone());
-                    eprintln!("asd");
-                },
-                "type": "text",
+                r#type: "text",
+                oninput: move |e| contents.set(e.value.clone()),
             }
         }
     })

+ 1 - 1
packages/core-macro/src/inlineprops.rs

@@ -99,8 +99,8 @@ impl ToTokens for InlinePropsBody {
         };
 
         out_tokens.append_all(quote! {
-            #[allow(non_camel_case)]
             #modifiers
+            #[allow(non_camel_case_types)]
             #vis struct #struct_name #generics {
                 #(#fields),*
             }

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

@@ -1,277 +0,0 @@
-use dioxus_core::prelude::*;
-use std::{
-    cell::{Cell, Ref, RefCell, RefMut},
-    fmt::{Debug, Display},
-    rc::Rc,
-};
-
-/// Store state between component renders!
-///
-/// ## Dioxus equivalent of useState, designed for Rust
-///
-/// The Dioxus version of `useState` for state management inside components. It allows you to ergonomically store and
-/// modify state between component renders. When the state is updated, the component will re-render.
-///
-/// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
-///
-/// [`use_state`] exposes a few helper methods to modify the underlying state:
-/// - `.set(new)` allows you to override the "work in progress" value with a new value
-/// - `.get_mut()` allows you to modify the WIP value
-/// - `.get_wip()` allows you to access the WIP value
-/// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
-///
-/// Additionally, a ton of std::ops traits are implemented for the `UseState` wrapper, meaning any mutative type operations
-/// will automatically be called on the WIP value.
-///
-/// ## Combinators
-///
-/// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality:
-/// - `.classic()` and `.split()`  convert the hook into the classic React-style hook
-///     ```rust
-///     let (state, set_state) = use_state(&cx, || 10).split()
-///     ```
-///
-///
-/// Usage:
-///
-/// ```ignore
-/// const Example: Component = |cx| {
-///     let counter = use_state(&cx, || 0);
-///
-///     cx.render(rsx! {
-///         div {
-///             h1 { "Counter: {counter}" }
-///             button { onclick: move |_| counter += 1, "Increment" }
-///             button { onclick: move |_| counter -= 1, "Decrement" }
-///         }
-///     ))
-/// }
-/// ```
-pub fn use_state<'a, T: 'static>(
-    cx: &'a ScopeState,
-    initial_state_fn: impl FnOnce() -> T,
-) -> UseState<'a, T> {
-    let hook = cx.use_hook(move |_| {
-        let first_val = initial_state_fn();
-        UseStateInner {
-            current_val: Rc::new(first_val),
-            update_callback: cx.schedule_update(),
-            wip: Rc::new(RefCell::new(None)),
-            update_scheuled: Cell::new(false),
-        }
-    });
-
-    hook.update_scheuled.set(false);
-    let mut new_val = hook.wip.borrow_mut();
-    if new_val.is_some() {
-        // if there's only one reference (weak or otherwise), we can just swap the values
-        if let Some(val) = Rc::get_mut(&mut hook.current_val) {
-            *val = new_val.take().unwrap();
-        } else {
-            hook.current_val = Rc::new(new_val.take().unwrap());
-        }
-    }
-
-    UseState { inner: &*hook }
-}
-struct UseStateInner<T: 'static> {
-    current_val: Rc<T>,
-    update_scheuled: Cell<bool>,
-    update_callback: Rc<dyn Fn()>,
-    wip: Rc<RefCell<Option<T>>>,
-}
-
-pub struct UseState<'a, T: 'static> {
-    inner: &'a UseStateInner<T>,
-}
-
-impl<T> Copy for UseState<'_, T> {}
-impl<'a, T> Clone for UseState<'a, T>
-where
-    T: 'static,
-{
-    fn clone(&self) -> Self {
-        UseState { inner: self.inner }
-    }
-}
-
-impl<T: Debug> Debug for UseState<'_, T> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{:?}", self.inner.current_val)
-    }
-}
-impl<'a, T: 'static> UseState<'a, T> {
-    /// Tell the Dioxus Scheduler that we need to be processed
-    pub fn needs_update(&self) {
-        if !self.inner.update_scheuled.get() {
-            self.inner.update_scheuled.set(true);
-            (self.inner.update_callback)();
-        }
-    }
-
-    pub fn set(&self, new_val: T) {
-        *self.inner.wip.borrow_mut() = Some(new_val);
-        self.needs_update();
-    }
-
-    pub fn get(&self) -> &'a T {
-        &self.inner.current_val
-    }
-
-    pub fn get_rc(&self) -> &'a Rc<T> {
-        &self.inner.current_val
-    }
-
-    /// Get the current status of the work-in-progress data
-    pub fn get_wip(&self) -> Ref<Option<T>> {
-        self.inner.wip.borrow()
-    }
-
-    /// Get the current status of the work-in-progress data
-    pub fn get_wip_mut(&self) -> RefMut<Option<T>> {
-        self.inner.wip.borrow_mut()
-    }
-
-    pub fn classic(self) -> (&'a T, Rc<dyn Fn(T)>) {
-        (&self.inner.current_val, self.setter())
-    }
-
-    pub fn setter(&self) -> Rc<dyn Fn(T)> {
-        let slot = self.inner.wip.clone();
-        Rc::new(move |new| {
-            *slot.borrow_mut() = Some(new);
-        })
-    }
-
-    pub fn for_async(self) -> UseState<'static, T> {
-        todo!()
-    }
-
-    pub fn wtih(self, f: impl FnOnce(&mut T)) {
-        let mut val = self.inner.wip.borrow_mut();
-
-        if let Some(inner) = val.as_mut() {
-            f(inner);
-        }
-    }
-}
-
-impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
-    /// Gain mutable access to the new value via [`RefMut`].
-    ///
-    /// If `modify` is called, then the component will re-render.
-    ///
-    /// This method is only available when the value is a `ToOwned` type.
-    ///
-    /// Mutable access is derived by calling "ToOwned" (IE cloning) on the current value.
-    ///
-    /// To get a reference to the current value, use `.get()`
-    pub fn modify(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.inner.wip.borrow_mut(), |slot| {
-            if slot.is_none() {
-                *slot = Some(self.inner.current_val.as_ref().to_owned());
-            }
-            slot.as_mut().unwrap()
-        })
-    }
-
-    pub fn inner(self) -> T {
-        self.inner.current_val.as_ref().to_owned()
-    }
-}
-
-impl<'a, T> std::ops::Deref for UseState<'a, T> {
-    type Target = T;
-
-    fn deref(&self) -> &Self::Target {
-        self.get()
-    }
-}
-
-// 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)
-    }
-}
-impl<'a, V, T: PartialEq<V>> PartialEq<V> for UseState<'a, T> {
-    fn eq(&self, other: &V) -> bool {
-        self.get() == other
-    }
-}
-impl<'a, O, T: std::ops::Not<Output = O> + Copy> std::ops::Not for UseState<'a, T> {
-    type Output = O;
-
-    fn not(self) -> Self::Output {
-        !*self.get()
-    }
-}
-
-/*
-
-Convenience methods for UseState.
-
-Note!
-
-This is not comprehensive.
-This is *just* meant to make common operations easier.
-*/
-
-use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
-
-impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
-    type Output = T;
-
-    fn add(self, rhs: T) -> Self::Output {
-        self.inner.current_val.add(rhs)
-    }
-}
-impl<'a, T: Copy + Add<T, Output = T>> AddAssign<T> for UseState<'a, T> {
-    fn add_assign(&mut self, rhs: T) {
-        self.set(self.inner.current_val.add(rhs));
-    }
-}
-impl<'a, T: Copy + Sub<T, Output = T>> Sub<T> for UseState<'a, T> {
-    type Output = T;
-
-    fn sub(self, rhs: T) -> Self::Output {
-        self.inner.current_val.sub(rhs)
-    }
-}
-impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
-    fn sub_assign(&mut self, rhs: T) {
-        self.set(self.inner.current_val.sub(rhs));
-    }
-}
-
-/// 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));
-    }
-}

+ 211 - 0
packages/hooks/src/usestate/handle.rs

@@ -0,0 +1,211 @@
+use super::owned::UseStateOwned;
+use std::{
+    cell::{Ref, RefMut},
+    fmt::{Debug, Display},
+    rc::Rc,
+};
+
+pub struct UseState<'a, T: 'static>(pub(crate) &'a UseStateOwned<T>);
+
+impl<T> Copy for UseState<'_, T> {}
+
+impl<'a, T: 'static> Clone for UseState<'a, T> {
+    fn clone(&self) -> Self {
+        UseState(self.0)
+    }
+}
+
+impl<T: Debug> Debug for UseState<'_, T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{:?}", self.0.current_val)
+    }
+}
+
+impl<'a, T: 'static> UseState<'a, T> {
+    /// Tell the Dioxus Scheduler that we need to be processed
+    pub fn needs_update(&self) {
+        if !self.0.update_scheuled.get() {
+            self.0.update_scheuled.set(true);
+            (self.0.update_callback)();
+        }
+    }
+
+    pub fn set(&self, new_val: T) {
+        *self.0.wip.borrow_mut() = Some(new_val);
+        self.needs_update();
+    }
+
+    pub fn get(&self) -> &'a T {
+        &self.0.current_val
+    }
+
+    pub fn get_rc(&self) -> &'a Rc<T> {
+        &self.0.current_val
+    }
+
+    /// Get the current status of the work-in-progress data
+    pub fn get_wip(&self) -> Ref<Option<T>> {
+        self.0.wip.borrow()
+    }
+
+    /// Get the current status of the work-in-progress data
+    pub fn get_wip_mut(&self) -> RefMut<Option<T>> {
+        self.0.wip.borrow_mut()
+    }
+
+    pub fn classic(self) -> (&'a T, Rc<dyn Fn(T)>) {
+        (&self.0.current_val, self.setter())
+    }
+
+    pub fn setter(&self) -> Rc<dyn Fn(T)> {
+        let slot = self.0.wip.clone();
+        Rc::new(move |new| *slot.borrow_mut() = Some(new))
+    }
+
+    pub fn wtih(&self, f: impl FnOnce(&mut T)) {
+        let mut val = self.0.wip.borrow_mut();
+
+        if let Some(inner) = val.as_mut() {
+            f(inner);
+        }
+    }
+
+    pub fn for_async(&self) -> UseStateOwned<T> {
+        let UseStateOwned {
+            current_val,
+            wip,
+            update_callback,
+            update_scheuled,
+        } = self.0;
+
+        UseStateOwned {
+            current_val: current_val.clone(),
+            wip: wip.clone(),
+            update_callback: update_callback.clone(),
+            update_scheuled: update_scheuled.clone(),
+        }
+    }
+}
+
+impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
+    /// Gain mutable access to the new value via [`RefMut`].
+    ///
+    /// If `modify` is called, then the component will re-render.
+    ///
+    /// This method is only available when the value is a `ToOwned` type.
+    ///
+    /// Mutable access is derived by calling "ToOwned" (IE cloning) on the current value.
+    ///
+    /// To get a reference to the current value, use `.get()`
+    pub fn modify(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.0.wip.borrow_mut(), |slot| {
+            if slot.is_none() {
+                *slot = Some(self.0.current_val.as_ref().to_owned());
+            }
+            slot.as_mut().unwrap()
+        })
+    }
+
+    pub fn inner(self) -> T {
+        self.0.current_val.as_ref().to_owned()
+    }
+}
+
+impl<'a, T> std::ops::Deref for UseState<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.get()
+    }
+}
+
+// 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.0.current_val)
+    }
+}
+impl<'a, V, T: PartialEq<V>> PartialEq<V> for UseState<'a, T> {
+    fn eq(&self, other: &V) -> bool {
+        self.get() == other
+    }
+}
+impl<'a, O, T: std::ops::Not<Output = O> + Copy> std::ops::Not for UseState<'a, T> {
+    type Output = O;
+
+    fn not(self) -> Self::Output {
+        !*self.get()
+    }
+}
+
+/*
+
+Convenience methods for UseState.
+
+Note!
+
+This is not comprehensive.
+This is *just* meant to make common operations easier.
+*/
+
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
+    type Output = T;
+
+    fn add(self, rhs: T) -> Self::Output {
+        self.0.current_val.add(rhs)
+    }
+}
+impl<'a, T: Copy + Add<T, Output = T>> AddAssign<T> for UseState<'a, T> {
+    fn add_assign(&mut self, rhs: T) {
+        self.set(self.0.current_val.add(rhs));
+    }
+}
+
+/// Sub
+impl<'a, T: Copy + Sub<T, Output = T>> Sub<T> for UseState<'a, T> {
+    type Output = T;
+
+    fn sub(self, rhs: T) -> Self::Output {
+        self.0.current_val.sub(rhs)
+    }
+}
+impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
+    fn sub_assign(&mut self, rhs: T) {
+        self.set(self.0.current_val.sub(rhs));
+    }
+}
+
+/// 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.0.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.0.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.0.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.0.current_val.div(rhs));
+    }
+}

+ 78 - 0
packages/hooks/src/usestate/mod.rs

@@ -0,0 +1,78 @@
+mod handle;
+mod owned;
+pub use handle::*;
+pub use owned::*;
+
+use dioxus_core::prelude::*;
+use std::{
+    cell::{Cell, RefCell},
+    rc::Rc,
+};
+
+/// Store state between component renders!
+///
+/// ## Dioxus equivalent of useState, designed for Rust
+///
+/// The Dioxus version of `useState` for state management inside components. It allows you to ergonomically store and
+/// modify state between component renders. When the state is updated, the component will re-render.
+///
+/// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
+///
+/// [`use_state`] exposes a few helper methods to modify the underlying state:
+/// - `.set(new)` allows you to override the "work in progress" value with a new value
+/// - `.get_mut()` allows you to modify the WIP value
+/// - `.get_wip()` allows you to access the WIP value
+/// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
+///
+/// Additionally, a ton of std::ops traits are implemented for the `UseState` wrapper, meaning any mutative type operations
+/// will automatically be called on the WIP value.
+///
+/// ## Combinators
+///
+/// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality:
+/// - `.classic()` and `.split()`  convert the hook into the classic React-style hook
+///     ```rust
+///     let (state, set_state) = use_state(&cx, || 10).split()
+///     ```
+///
+///
+/// Usage:
+///
+/// ```ignore
+/// const Example: Component = |cx| {
+///     let counter = use_state(&cx, || 0);
+///
+///     cx.render(rsx! {
+///         div {
+///             h1 { "Counter: {counter}" }
+///             button { onclick: move |_| counter += 1, "Increment" }
+///             button { onclick: move |_| counter -= 1, "Decrement" }
+///         }
+///     ))
+/// }
+/// ```
+pub fn use_state<'a, T: 'static>(
+    cx: &'a ScopeState,
+    initial_state_fn: impl FnOnce() -> T,
+) -> UseState<'a, T> {
+    let hook = cx.use_hook(move |_| UseStateOwned {
+        current_val: Rc::new(initial_state_fn()),
+        update_callback: cx.schedule_update(),
+        wip: Rc::new(RefCell::new(None)),
+        update_scheuled: Cell::new(false),
+    });
+
+    hook.update_scheuled.set(false);
+    let mut new_val = hook.wip.borrow_mut();
+
+    if new_val.is_some() {
+        // if there's only one reference (weak or otherwise), we can just swap the values
+        if let Some(val) = Rc::get_mut(&mut hook.current_val) {
+            *val = new_val.take().unwrap();
+        } else {
+            hook.current_val = Rc::new(new_val.take().unwrap());
+        }
+    }
+
+    UseState(hook)
+}

+ 102 - 0
packages/hooks/src/usestate/owned.rs

@@ -0,0 +1,102 @@
+use std::{
+    cell::{Cell, Ref, RefCell, RefMut},
+    fmt::{Debug, Display},
+    rc::Rc,
+};
+pub struct UseStateOwned<T: 'static> {
+    // this will always be outdated
+    pub(crate) current_val: Rc<T>,
+    pub(crate) wip: Rc<RefCell<Option<T>>>,
+    pub(crate) update_callback: Rc<dyn Fn()>,
+    pub(crate) update_scheuled: Cell<bool>,
+}
+
+impl<T> UseStateOwned<T> {
+    pub fn get(&self) -> Ref<T> {
+        Ref::map(self.wip.borrow(), |x| x.as_ref().unwrap())
+    }
+
+    pub fn set(&self, new_val: T) {
+        *self.wip.borrow_mut() = Some(new_val);
+        (self.update_callback)();
+    }
+
+    pub fn modify(&self) -> RefMut<T> {
+        RefMut::map(self.wip.borrow_mut(), |x| x.as_mut().unwrap())
+    }
+
+    pub fn with(&self, f: impl FnOnce(&mut T)) {
+        //
+    }
+}
+
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+impl<T: Debug> Debug for UseStateOwned<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{:?}", self.current_val)
+    }
+}
+
+// enable displaty for the handle
+impl<'a, T: 'static + Display> std::fmt::Display for UseStateOwned<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.current_val)
+    }
+}
+
+impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseStateOwned<T> {
+    type Output = T;
+
+    fn add(self, rhs: T) -> Self::Output {
+        self.current_val.add(rhs)
+    }
+}
+
+impl<'a, T: Copy + Add<T, Output = T>> AddAssign<T> for UseStateOwned<T> {
+    fn add_assign(&mut self, rhs: T) {
+        self.set(self.current_val.add(rhs));
+    }
+}
+
+/// Sub
+impl<'a, T: Copy + Sub<T, Output = T>> Sub<T> for UseStateOwned<T> {
+    type Output = T;
+
+    fn sub(self, rhs: T) -> Self::Output {
+        self.current_val.sub(rhs)
+    }
+}
+impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseStateOwned<T> {
+    fn sub_assign(&mut self, rhs: T) {
+        self.set(self.current_val.sub(rhs));
+    }
+}
+
+/// MUL
+impl<'a, T: Copy + Mul<T, Output = T>> Mul<T> for UseStateOwned<T> {
+    type Output = T;
+
+    fn mul(self, rhs: T) -> Self::Output {
+        self.current_val.mul(rhs)
+    }
+}
+impl<'a, T: Copy + Mul<T, Output = T>> MulAssign<T> for UseStateOwned<T> {
+    fn mul_assign(&mut self, rhs: T) {
+        self.set(self.current_val.mul(rhs));
+    }
+}
+
+/// DIV
+impl<'a, T: Copy + Div<T, Output = T>> Div<T> for UseStateOwned<T> {
+    type Output = T;
+
+    fn div(self, rhs: T) -> Self::Output {
+        self.current_val.div(rhs)
+    }
+}
+impl<'a, T: Copy + Div<T, Output = T>> DivAssign<T> for UseStateOwned<T> {
+    fn div_assign(&mut self, rhs: T) {
+        self.set(self.current_val.div(rhs));
+    }
+}