Selaa lähdekoodia

wip: some docs and suspense

Jonathan Kelley 3 vuotta sitten
vanhempi
commit
93d4b8c

+ 2 - 2
Cargo.toml

@@ -93,5 +93,5 @@ name = "tailwind"
 path = "./examples/tailwind.rs"
 
 
-[patch.crates-io]
-wasm-bindgen = { path = "../Tinkering/wasm-bindgen" }
+# [patch.crates-io]
+# wasm-bindgen = { path = "../Tinkering/wasm-bindgen" }

+ 13 - 10
docs/guide/src/README.md

@@ -5,7 +5,7 @@
 **Dioxus** is a framework and ecosystem for building fast, scalable, and robust user interfaces with the Rust programming language. This guide will help you get up-and-running with Dioxus running on the Web, Desktop, Mobile, and more.
 
 ```rust
-fn App((cx, props): Component<()>) -> Element {
+fn App(cx: Context, props: &()) -> Element {
     let mut count = use_state(cx, || 0);
 
     cx.render(rsx!(
@@ -16,11 +16,20 @@ fn App((cx, props): Component<()>) -> Element {
 };
 ```
 
-The Dioxus API and patterns closely resemble React - if this guide is lacking in any general concept or an error message is confusing, we recommend substituting "React" for "Dioxus" in your web search terms. A major goal of Dioxus is to provide a familiar toolkit for UI in Rust, so we've chosen to follow in the footsteps of popular UI frameworks (React, Redux, etc) - if you know React, then you already know Dioxus. If you don't know either, this guide will still help you!
-
+In general, Dioxus and React share many functional similarities. If this guide is lacking in any general concept or an error message is confusing, React's documentation might be more helpful. We are dedicated to providing a *familiar* toolkit for UI in Rust, so we've chosen to follow in the footsteps of popular UI frameworks (React, Redux, etc). If you know React, then you already know Dioxus. If you don't know either, this guide will still help you!
 
 > This is introduction book! For advanced topics, check out the [Reference Guide]() instead.
 
+## Multiplatform
+
+Dioxus is a *portable* toolkit, meaning the Core implementation can run anywhere with no platform-dependent linking. Unlike many other Rust frontend toolkits, Dioxus is not intrinsically linked to Web-Sys. In fact, every element and event listener can be swapped out at compile time. By default, Dioxus ships with the `Html` feature enabled which can be disabled depending on your target renderer.
+
+Right now, we have several 1st-party renderers:
+- WebSys (for WASM)
+- Tao/Tokio (for Desktop apps)
+- Tao/Tokio (for Mobile apps)
+- SSR (for generating static markup)
+
 ### Web Support
 ---
 
@@ -39,12 +48,7 @@ Examples:
 
 ### SSR Support
 ---
-Dioxus supports server-side rendering! In a pinch, you can literally "debug" the VirtualDom:
-
-```rust
-let dom = VirtualDom::new(App);
-println!("{:?}, dom");
-```
+Dioxus supports server-side rendering! 
 
 For rendering statically to an `.html` file or from a WebServer, then you'll want to make sure the `ssr` feature is enabled in the `dioxus` crate and use the `dioxus::ssr` API. We don't expect the SSR API to change drastically in the future.
 
@@ -52,7 +56,6 @@ For rendering statically to an `.html` file or from a WebServer, then you'll wan
 let contents = dioxus::ssr::render_vdom(&dom, |c| c);
 ```
 
-
 [Jump to the getting started guide for SSR.]()
 
 Examples:

+ 11 - 10
docs/guide/src/concepts/00-index.md

@@ -26,7 +26,7 @@ container.push(green_light);
 container.push(yellow_light);
 container.push(red_light);
 
-container.onclick(move |_| {
+container.set_onclick(move |_| {
     if red_light.enabled() {
         red_light.set_enabled(false);
         green_light.set_enabled(true);
@@ -45,22 +45,23 @@ As the UI grows in scale, our logic to keep each element in the proper state wou
 Instead, with Dioxus, we *declare* what we want our UI to look like:
 
 ```rust
-let state = "red";
+let mut state = use_state(cx, || "red");
 
-rsx!(
+cx.render(rsx!(
     Container {
-        Light { color: "red", enabled: {state == "red"}  }
-        Light { color: "yellow", enabled: {state == "yellow"}  }
-        Light { color: "green", enabled: {state == "green"}  }
-        onclick: |_| {
-            state = match state {
+        Light { color: "red", enabled: format_args!("{}", state == "red")  }
+        Light { color: "yellow", enabled: format_args!("{}", state == "yellow") }
+        Light { color: "green", enabled: format_args!("{}", state == "green") }
+
+        onclick: move |_| {
+            state.set(match *state {
                 "green" => "yellow",
                 "yellow" => "red",
                 "red" => "green",
-            }
+            })
         }
     }
-)
+))
 ```
 
 Remember: this concept is not new! Many frameworks are declarative - with React being the most popular. Declarative frameworks tend to be much more enjoyable to work with than imperative frameworks.

+ 5 - 5
docs/guide/src/concepts/components.md

@@ -84,7 +84,7 @@ struct PostProps {
 
 And our render function:
 ```rust
-fn Post((cx, props): Scope<PostProps>) -> Element {
+fn Post(cx: Context, props: &PostProps) -> Element {
     cx.render(rsx!{
         div { class: "post-container"
             VoteButton {
@@ -120,7 +120,7 @@ struct VoteButtonProps {
     score: i32
 }
 
-fn VoteButton((cx, props): Scope<VoteButtonProps>) -> Element {
+fn VoteButton(cx: Context, props: &VoteButtonProps) -> Element {
     cx.render(rsx!{
         div { class: "votebutton"
             div { class: "arrow up" }
@@ -145,7 +145,7 @@ struct TitleCardProps<'a> {
     title: &'a str,
 }
 
-fn TitleCard<'a>((cx, props): Scope<'a, TitleCardProps>) -> Element<'a> {
+fn TitleCard(cx: Context, props: &TitleCardProps) -> Element {
     cx.render(rsx!{
         h1 { "{props.title}" }
     })
@@ -174,8 +174,8 @@ function Component(props) {
 Because Dioxus needs to work with the rules of Rust it uses the `Context` object to maintain some internal bookkeeping. That's what the `Context` object is: a place for the component to store state, manage listeners, and allocate elements. Advanced users of Dioxus will want to learn how to properly leverage the `Context` object to build robust and performant extensions for Dioxus.
 
 ```rust
-fn Post((cx /* <-- our Context object*/, props): Scope<PostProps>) -> Element {
-    cx.render(rsx!{ })
+fn Post(cx: Context, props: &PostProps) -> Element {
+    cx.render(rsx!("hello"))
 }
 ```
 ## Moving forward

+ 7 - 7
docs/guide/src/concepts/conditional_rendering.md

@@ -26,7 +26,7 @@ struct AppProps {
 Now that we have a "logged_in" flag accessible in our props, we can render two different screens:
 
 ```rust
-fn App((cx, props): Scope<AppProps>) -> Element {
+fn App(cx: Context, props: &AppProps) -> Element {
     if props.logged_in {
         cx.render(rsx!{
             DashboardScreen {}
@@ -48,7 +48,7 @@ Rust provides us algebraic datatypes: enums that can contain values. Using the `
 For instance, we could run a function that returns a Result:
 
 ```rust
-fn App((cx, props): Scope<()>) -> Element {
+fn App(cx: Context, props: &())-> Element {
     match get_name() {
         Ok(name) => cx.render(rsx!( "Hello, {name}!" )),
         Err(err) => cx.render(rsx!( "Sorry, I don't know your name, because an error occurred: {err}" )),
@@ -58,7 +58,7 @@ fn App((cx, props): Scope<()>) -> Element {
 
 We can even match against values:
 ```rust
-fn App((cx, props): Scope<()>) -> Element {
+fn App(cx: Context, props: &())-> Element {
     match get_name() {
         "jack" => cx.render(rsx!( "Hey Jack, how's Diane?" )),
         "diane" => cx.render(rsx!( "Hey Diana, how's Jack?" )),
@@ -67,12 +67,12 @@ fn App((cx, props): Scope<()>) -> Element {
 }
 ```
 
-Do note: the `rsx!` macro returns a `Closure`, an anonymous function that has a unique type. 
+Do note: the `rsx!` macro returns a `Closure`, an anonymous function that has a unique type. To turn our `rsx!` into Elements, we need to call `cx.render`.
 
 To make patterns like these less verbose, the `rsx!` macro accepts an optional first argument on which it will call `render`. Our previous component can be shortened with this alternative syntax:
 
 ```rust
-fn App((cx, props): Scope<()>) -> Element {
+fn App(cx: Context, props: &())-> Element {
     match get_name() {
         "jack" => rsx!(cx, "Hey Jack, how's Diane?" ),
         "diane" => rsx!(cx, "Hey Diana, how's Jack?" ),
@@ -83,13 +83,13 @@ fn App((cx, props): Scope<()>) -> Element {
 
 This syntax even enables us to write a one-line component:
 ```rust
-static App: Fc<()> = |cx, props| rsx!(cx, "hello world!");
+static App: FC<()> = |cx, props| 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
-fn App((cx, props): Scope<()>) -> Element {
+fn App(cx: Context, props: &())-> Element {
     let greeting = match get_name() {
         "jack" => rsx!("Hey Jack, how's Diane?" ),
         "diane" => rsx!("Hey Diana, how's Jack?" ),

+ 7 - 7
docs/guide/src/concepts/interactivity.md

@@ -35,7 +35,7 @@ fn main() {
 When Dioxus renders your app, it will pass an immutable reference of `PostProps` to your `Post` component. Here, you can pass the state down into children.
 
 ```rust
-fn App((cx, props): Scope<PostProps>) -> Element {
+fn App(cx: Context, props: &PostProps) -> Element {
     cx.render(rsx!{
         Title { title: &props.title }
         Score { score: &props.score }
@@ -63,8 +63,8 @@ Instead, you'll want to store state internally in your components and let *that*
 The most common hook you'll use for storing state is `use_state`. `use_state` provides a slot for some data that allows you to read and update the value without accidentally mutating it.
 
 ```rust
-fn App((cx, props): Scope<()>) -> Element {
-    let post = use_state(|| {
+fn App(cx: Context, props: &())-> Element {
+    let post = use_state(cx, || {
         PostData {
             id: Uuid::new_v4(),
             score: 10,
@@ -111,8 +111,8 @@ When responding to user-triggered events, we'll want to "listen" for an event on
 For example, let's say we provide a button to generate a new post. Whenever the user clicks the button, they get a new post. To achieve this functionality, we'll want to attach a function to the `on_click` method of `button`. Whenever the button is clicked, our function will run, and we'll get new Post data to work with.
 
 ```rust
-fn App((cx, props): Scope<()>) -> Element {
-    let post = use_state(|| PostData::new());
+fn App(cx: Context, props: &())-> Element {
+    let post = use_state(cx, || PostData::new());
 
     cx.render(rsx!{
         button {
@@ -134,8 +134,8 @@ We can use tasks in our components to build a tiny stopwatch that ticks every se
 
 ```rust
 
-fn App((cx, props): Scope<()>) -> Element {
-    let mut sec_elapsed = use_state(|| 0);
+fn App(cx: Context, props: &())-> Element {
+    let mut sec_elapsed = use_state(cx, || 0);
 
     cx.spawn_task(async move {
         TimeoutFuture::from_ms(1000).await;

+ 1 - 1
docs/guide/src/concepts/lists.md

@@ -74,7 +74,7 @@ struct PostListProps<'a> {
 Next, we're going to define our component:
 
 ```rust
-fn App((cx, props): Component<PostList>) -> Element {
+fn App(cx: Context, props: &PostList) -> Element {
     // First, we create a new iterator by mapping the post array
     let posts = props.posts.iter().map(|post| rsx!{
         Post {

+ 53 - 0
docs/guide/src/concepts/suspense.md

@@ -1 +1,54 @@
 # Suspense
+
+Suspense in Dioxus is enabled through placeholder nodes.
+
+just a component that renders nodes into the placeholder after the future is finished?
+
+in react, suspense is just completely pausing diffing while a 
+
+```rust
+let n = use_suspense(cx || {
+    cx.render(rsx!{
+        Suspense {
+            prom: fut,
+            callback: || {}
+        }
+    })
+})
+
+suspense () {
+    let value = use_state();
+    if first_render {
+        push_task({
+            value.set(fut.await);
+        });
+    } else {
+        callback(value)
+    }
+}
+
+
+let name = fetch_name().await;
+
+
+function ProfileDetails() {
+  // Try to read user info, although it might not have loaded yet
+  const user = resource.read();
+  return <h1>{user.name}</h1>;
+}
+
+
+fn ProfileDteails() {
+    let user = resource.suspend(cx, |l| rsx!("{l}"));
+
+    // waits for the resource to be ready and updates the placeholder with the tree
+    let name = resource.suspend_with(cx, |val| rsx!( div { "hello" "{user}" } ));
+
+    cx.render(rsx!(
+        div {
+            {user}
+            {name}
+        }
+    ))
+}
+```

+ 1 - 1
docs/guide/src/concepts/vnodes.md

@@ -107,7 +107,7 @@ To do this, we use the familiar struct-style syntax that Rust provides. Commas a
 ```rust
 rsx!(
     div {
-        hidden: true,
+        hidden: "true",
         background_color: "blue",
         class: "card color-{mycolor}"
     }

+ 15 - 25
docs/guide/src/hello_world.md

@@ -1,4 +1,4 @@
-# Hello World
+# Hello, World desktop app
 
 Let's put together a simple "hello world" desktop application to get acquainted with Dioxus. 
 
@@ -92,10 +92,10 @@ use dioxus::prelude::*;
 
 
 fn main() {
-    dioxus::desktop::start(App, |c| c);
+    dioxus::desktop::launch(App, |c| c);
 }
 
-fn App((cx, props): Component<()>) -> Element {
+fn App(cx: Context, props: &()) -> Element {
     cx.render(rsx! (
         div { "Hello, world!" }
     ))
@@ -108,7 +108,7 @@ At this point, you could call `cargo run` and be greeted with a simple `Hello, W
 
 ### Dissecting our example
 
-This bit of code imports everything from the the `prelude` module. This brings into scope the right traits, types, and macros needed for working with Dioxus.
+The `use` statement at the top of our app imports everything from the the `prelude` module. `use`-ing the prelude imports the right traits, types, and macros needed for working with Dioxus.
 
 ```rust
 use dioxus::prelude::*;
@@ -118,44 +118,34 @@ This initialization code launches a Tokio runtime on a helper thread where your
 
 ```rust
 fn main() {
-    dioxus::desktop::start(App, |c| c);
+    dioxus::desktop::launch(App, |c| c);
 }
 ```
 
 Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Element`.
 
 ```rust
-fn App((cx, props): Component<()>) -> Element {
+fn App(cx: Context, props: &()) -> Element {
     cx.render(rsx! {
         div { "Hello, world!" }
     })    
 }
 ```
-In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax:
 
-```rust
-fn App<'a>(cx: Component<'a, ()>) -> Element<'a> {
-    cx.render(rsx! {
-        div { "Hello, world!" }
-    })
-}
-```
+Writing `fn App(cx: Context, props: &()) -> 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.
 
-Writing `fn App((cx, props): Component<()>) -> 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: Fc<()> = |cx, props| {
-    cx.render(rsx! {
-        div { "Hello, world!" }
-    })
-};
+static App: FC<()> = |cx, props| cx.render(rsx!(div { "Hello, world!" }));
 ```
 
-### The `Context` object
+### What is this `Context` object?
+
+Coming from React, the `Context` 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. 
+
+In Dioxus, you are given an explicit `Context` object to control how the component renders and stores data. The `Context` object provides a handful of useful APIs for features like suspense, rendering, and more.
 
-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. In Dioxus, you are given an explicit `Context` object to control how the component renders and stores data.
+## Moving on
 
-### The `rsx!` macro
+Congrats! You've built your first desktop application with Dioxus. Next, we're going to learn about the basics of building interactive user interfaces.
 
-Next, we're greeted with the `rsx!` macro. This lets us add a custom DSL for declaratively building the structure of our app. The semantics of this macro are similar to that of JSX and HTML, though with a familiar Rust-y interface. The `html!` macro is also available for writing components with a JSX/HTML syntax.
 
-The `rsx!` macro is lazy: it does not immediately produce elements or allocates, but rather builds a closure which can be rendered with `cx.render`.

+ 10 - 0
docs/guide/src/setup.md

@@ -20,6 +20,16 @@ Dioxus requires a few main things to get up and running:
 
 Dioxus integrates very well with the Rust-Analyzer IDE plugin which will provide appropriate syntax highlighting, code navigation, folding, and more.
 
+### Installing Rust
+
+Head over to [https://rust-lang.org](http://rust-lang.org) and install the Rust compiler. 
+
+Once installed, make sure to  install wasm32-unknown-unknown as a target if you're planning on deploying your app to the web.
+
+```
+rustup target add wasm32-unknown-uknown
+```
+
 ### Dioxus-CLI for dev server, bundling, etc.
 
 We also recommend installing the Dioxus CLI. The Dioxus CLI automates building and packaging for various targets and integrates with simulators, development servers, and app deployment. To install the CLI, you'll need cargo (should be automatically installed with Rust):

+ 0 - 4
packages/core/Cargo.toml

@@ -67,7 +67,3 @@ harness = false
 [[example]]
 name = "rows"
 path = "./examples/rows.rs"
-
-
-[profile.release]
-debug = true

+ 20 - 0
packages/core/README.md

@@ -56,3 +56,23 @@ There's a few invariants that are very important:
 
 - References to `ScopeInner` and `Props` passed into components are *always* valid for as long as the component exists. Even if the scope backing is resized to fit more scopes, the scope has to stay the same place in memory.
 
+
+
+## Suspense
+
+Suspense is done through combinators on values. 
+
+```rust
+let name = get_name(cx).suspend();
+
+rsx!(
+    div {
+        {name}
+        div {
+            div {
+    
+            }
+        }
+    }
+)
+```

+ 18 - 66
packages/core/src/diff.rs

@@ -284,7 +284,7 @@ impl<'bump> DiffState<'bump> {
     // recursively push all the nodes of a tree onto the stack and return how many are there
     fn push_all_nodes(&mut self, node: &'bump VNode<'bump>) -> usize {
         match node {
-            VNode::Text(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
+            VNode::Text(_) | VNode::Placeholder(_) => {
                 self.mutations.push_root(node.mounted_id());
                 1
             }
@@ -354,8 +354,7 @@ impl<'bump> DiffState<'bump> {
     fn create_node(&mut self, node: &'bump VNode<'bump>) {
         match node {
             VNode::Text(vtext) => self.create_text_node(vtext, node),
-            VNode::Suspended(suspended) => self.create_suspended_node(suspended, node),
-            VNode::Anchor(anchor) => self.create_anchor_node(anchor, node),
+            VNode::Placeholder(anchor) => self.create_anchor_node(anchor, node),
             VNode::Element(element) => self.create_element_node(element, node),
             VNode::Fragment(frag) => self.create_fragment_node(frag),
             VNode::Component(component) => self.create_component_node(component),
@@ -371,17 +370,7 @@ impl<'bump> DiffState<'bump> {
         self.stack.add_child_count(1);
     }
 
-    fn create_suspended_node(&mut self, suspended: &'bump VSuspended, node: &'bump VNode<'bump>) {
-        let real_id = self.scopes.reserve_node(node);
-        self.mutations.create_placeholder(real_id);
-
-        suspended.dom_id.set(Some(real_id));
-        self.stack.add_child_count(1);
-
-        self.attach_suspended_node_to_scope(suspended);
-    }
-
-    fn create_anchor_node(&mut self, anchor: &'bump VAnchor, node: &'bump VNode<'bump>) {
+    fn create_anchor_node(&mut self, anchor: &'bump VPlaceholder, node: &'bump VNode<'bump>) {
         let real_id = self.scopes.reserve_node(node);
 
         self.mutations.create_placeholder(real_id);
@@ -433,12 +422,10 @@ impl<'bump> DiffState<'bump> {
             self.mutations.set_attribute(attr, real_id.as_u64());
         }
 
-        if !children.is_empty() {
-            if children.len() == 1 {
-                if let VNode::Text(vtext) = children[0] {
-                    self.mutations.set_text(vtext.text, real_id.as_u64());
-                    return;
-                }
+        if !children.is_empty() && children.len() == 1 {
+            if let VNode::Text(vtext) = children[0] {
+                self.mutations.set_text(vtext.text, real_id.as_u64());
+                return;
             }
         }
 
@@ -529,17 +516,14 @@ impl<'bump> DiffState<'bump> {
                 self.diff_component_nodes(old_node, new_node, old, new)
             }
             (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
-            (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
-            (Suspended(old), Suspended(new)) => self.diff_suspended_nodes(old, new),
+            (Placeholder(old), Placeholder(new)) => new.dom_id.set(old.dom_id.get()),
             (Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
             (Linked(old), Linked(new)) => self.diff_linked_nodes(old, new),
 
             // Anything else is just a basic replace and create
             (
-                Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_)
-                | Suspended(_),
-                Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_)
-                | Suspended(_),
+                Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
+                Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
             ) => self
                 .stack
                 .create_node(new_node, MountType::Replace { old: old_node }),
@@ -664,7 +648,7 @@ impl<'bump> DiffState<'bump> {
                             self.mutations.set_text(new_text.text, root.as_u64());
                         }
                     }
-                    (VNode::Text(old_text), _) => {
+                    (VNode::Text(_old_text), _) => {
                         self.stack.element_stack.push(root);
                         self.stack.instructions.push(DiffInstruction::PopElement);
                         self.stack.create_node(new1, MountType::Append);
@@ -770,11 +754,6 @@ impl<'bump> DiffState<'bump> {
         self.diff_children(old.children, new.children);
     }
 
-    fn diff_suspended_nodes(&mut self, old: &'bump VSuspended, new: &'bump VSuspended) {
-        new.dom_id.set(old.dom_id.get());
-        self.attach_suspended_node_to_scope(new);
-    }
-
     fn diff_linked_nodes(&mut self, old: &'bump NodeLink, new: &'bump NodeLink) {
         if !std::ptr::eq(old.node, new.node) {
             // if the ptrs are the same then theyr're the same
@@ -818,14 +797,14 @@ impl<'bump> DiffState<'bump> {
             (_, []) => {
                 self.remove_nodes(old, true);
             }
-            ([VNode::Anchor(old_anchor)], [VNode::Anchor(new_anchor)]) => {
+            ([VNode::Placeholder(old_anchor)], [VNode::Placeholder(new_anchor)]) => {
                 old_anchor.dom_id.set(new_anchor.dom_id.get());
             }
-            ([VNode::Anchor(_)], _) => {
+            ([VNode::Placeholder(_)], _) => {
                 self.stack
                     .create_children(new, MountType::Replace { old: &old[0] });
             }
-            (_, [VNode::Anchor(_)]) => {
+            (_, [VNode::Placeholder(_)]) => {
                 let new: &'bump VNode<'bump> = &new[0];
                 if let Some(first_old) = old.get(0) {
                     self.remove_nodes(&old[1..], true);
@@ -1227,8 +1206,7 @@ impl<'bump> DiffState<'bump> {
             match &search_node.take().unwrap() {
                 VNode::Text(t) => break t.dom_id.get(),
                 VNode::Element(t) => break t.dom_id.get(),
-                VNode::Suspended(t) => break t.dom_id.get(),
-                VNode::Anchor(t) => break t.dom_id.get(),
+                VNode::Placeholder(t) => break t.dom_id.get(),
                 VNode::Linked(l) => {
                     let node: &VNode = unsafe { std::mem::transmute(&*l.node) };
                     self.find_last_element(node);
@@ -1263,8 +1241,7 @@ impl<'bump> DiffState<'bump> {
                 }
                 VNode::Text(t) => break t.dom_id.get(),
                 VNode::Element(t) => break t.dom_id.get(),
-                VNode::Suspended(t) => break t.dom_id.get(),
-                VNode::Anchor(t) => break t.dom_id.get(),
+                VNode::Placeholder(t) => break t.dom_id.get(),
             }
         }
     }
@@ -1282,7 +1259,7 @@ impl<'bump> DiffState<'bump> {
                 self.remove_nodes(el.children, false);
             }
 
-            VNode::Text(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
+            VNode::Text(_) | VNode::Placeholder(_) => {
                 let id = old
                     .try_mounted_id()
                     .unwrap_or_else(|| panic!("broke on {:?}", old));
@@ -1331,15 +1308,7 @@ impl<'bump> DiffState<'bump> {
                         }
                     }
                 }
-                VNode::Suspended(s) => {
-                    let id = s.dom_id.get().unwrap();
-                    self.scopes.collect_garbage(id);
-
-                    if gen_muts {
-                        self.mutations.remove(id.as_u64());
-                    }
-                }
-                VNode::Anchor(a) => {
+                VNode::Placeholder(a) => {
                     let id = a.dom_id.get().unwrap();
                     self.scopes.collect_garbage(id);
 
@@ -1386,21 +1355,4 @@ impl<'bump> DiffState<'bump> {
         let long_listener = unsafe { std::mem::transmute(listener) };
         scope.items.borrow_mut().listeners.push(long_listener)
     }
-
-    fn attach_suspended_node_to_scope(&mut self, suspended: &'bump VSuspended) {
-        if let Some(scope) = self
-            .stack
-            .current_scope()
-            .and_then(|id| self.scopes.get_scope(&id))
-        {
-            todo!()
-            // // safety: this lifetime is managed by the logic on scope
-            // let extended = unsafe { std::mem::transmute(suspended) };
-            // scope
-            //     .items
-            //     .borrow_mut()
-            //     .suspended_nodes
-            //     .insert(suspended.task_id, extended);
-        }
-    }
 }

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

@@ -38,7 +38,7 @@ pub(crate) mod innerlude {
 pub use crate::innerlude::{
     Attribute, Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, IntoVNode,
     LazyNodes, Listener, MountType, Mutations, NodeFactory, Properties, SchedulerMsg, ScopeId,
-    UserEvent, VAnchor, VElement, VFragment, VNode, VSuspended, VirtualDom, FC,
+    UserEvent, VElement, VFragment, VNode, VPlaceholder, VSuspended, VirtualDom, FC,
 };
 
 pub mod prelude {

+ 12 - 25
packages/core/src/nodes.rs

@@ -97,19 +97,9 @@ pub enum VNode<'src> {
     /// ```
     Component(&'src VComponent<'src>),
 
-    /// Suspended VNodes represent chunks of the UI tree that are not yet ready to be displayed.
+    /// Placeholders are a type of placeholder VNode used when fragments don't contain any children.
     ///
-    /// # Example
-    ///
-    /// ```rust, ignore
-    ///
-    ///
-    /// ```
-    Suspended(&'src VSuspended),
-
-    /// Anchors are a type of placeholder VNode used when fragments don't contain any children.
-    ///
-    /// Anchors cannot be directly constructed via public APIs.
+    /// Placeholders cannot be directly constructed via public APIs.
     ///
     /// # Example
     ///
@@ -123,7 +113,7 @@ pub enum VNode<'src> {
     ///     assert_eq!(root, VNode::Anchor);
     /// }
     /// ```
-    Anchor(&'src VAnchor),
+    Placeholder(&'src VPlaceholder),
 
     /// A VNode that is actually a pointer to some nodes rather than the nodes directly. Useful when rendering portals
     /// or eliding lifetimes on VNodes through runtime checks.
@@ -151,8 +141,7 @@ impl<'src> VNode<'src> {
             VNode::Fragment(f) => f.key,
 
             VNode::Text(_t) => None,
-            VNode::Suspended(_s) => None,
-            VNode::Anchor(_f) => None,
+            VNode::Placeholder(_f) => None,
             VNode::Linked(_c) => None,
         }
     }
@@ -171,8 +160,7 @@ impl<'src> VNode<'src> {
         match &self {
             VNode::Text(el) => el.dom_id.get(),
             VNode::Element(el) => el.dom_id.get(),
-            VNode::Anchor(el) => el.dom_id.get(),
-            VNode::Suspended(el) => el.dom_id.get(),
+            VNode::Placeholder(el) => el.dom_id.get(),
 
             VNode::Linked(_) => None,
             VNode::Fragment(_) => None,
@@ -194,8 +182,7 @@ impl<'src> VNode<'src> {
             VNode::Text(t) => VNode::Text(*t),
             VNode::Element(e) => VNode::Element(*e),
             VNode::Component(c) => VNode::Component(*c),
-            VNode::Suspended(s) => VNode::Suspended(*s),
-            VNode::Anchor(a) => VNode::Anchor(*a),
+            VNode::Placeholder(a) => VNode::Placeholder(*a),
             VNode::Fragment(f) => VNode::Fragment(VFragment {
                 children: f.children,
                 key: f.key,
@@ -219,12 +206,11 @@ impl Debug for VNode<'_> {
                 .finish(),
 
             VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
-            VNode::Anchor(_) => write!(s, "VNode::VAnchor"),
+            VNode::Placeholder(_) => write!(s, "VNode::VAnchor"),
 
             VNode::Fragment(frag) => {
                 write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
             }
-            VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
             VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
             VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
         }
@@ -254,7 +240,7 @@ fn empty_cell() -> Cell<Option<ElementId>> {
 }
 
 /// A placeholder node only generated when Fragments don't have any children.
-pub struct VAnchor {
+pub struct VPlaceholder {
     pub dom_id: Cell<Option<ElementId>>,
 }
 
@@ -357,6 +343,7 @@ pub struct Listener<'bump> {
     pub event: &'static str,
 
     /// The actual callback that the user specified
+    #[allow(clippy::type_complexity)]
     pub(crate) callback:
         RefCell<Option<BumpBox<'bump, dyn FnMut(std::sync::Arc<dyn Any + Send + Sync>) + 'bump>>>,
 }
@@ -655,7 +642,7 @@ impl<'a> NodeFactory<'a> {
         }
 
         if nodes.is_empty() {
-            nodes.push(VNode::Anchor(bump.alloc(VAnchor {
+            nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
                 dom_id: empty_cell(),
             })));
         }
@@ -678,7 +665,7 @@ impl<'a> NodeFactory<'a> {
         }
 
         if nodes.is_empty() {
-            nodes.push(VNode::Anchor(bump.alloc(VAnchor {
+            nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
                 dom_id: empty_cell(),
             })));
         }
@@ -722,7 +709,7 @@ impl<'a> NodeFactory<'a> {
         }
 
         if nodes.is_empty() {
-            nodes.push(VNode::Anchor(bump.alloc(VAnchor {
+            nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
                 dom_id: empty_cell(),
             })));
         }

+ 2 - 41
packages/core/src/scope.rs

@@ -1,7 +1,6 @@
 use crate::innerlude::*;
 
 use futures_channel::mpsc::UnboundedSender;
-use fxhash::FxHashMap;
 use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
@@ -72,7 +71,6 @@ pub struct Scope {
 pub struct SelfReferentialItems<'a> {
     pub(crate) listeners: Vec<&'a Listener<'a>>,
     pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
-    pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended>,
     pub(crate) tasks: Vec<BumpBox<'a, dyn Future<Output = ()>>>,
 }
 
@@ -347,31 +345,6 @@ impl Scope {
         Some(link)
     }
 
-    pub fn suspend<'src, F: Future<Output = Element> + 'src>(
-        &'src self,
-        mut fut: impl FnMut() -> F,
-    ) -> Option<VNode> {
-        let channel = self.sender.clone();
-        let node_fut = fut();
-
-        let scope = self.scope_id();
-
-        // self.push_task(move || {
-        //
-        // async move {
-        //     //
-        //     let r = node_fut.await;
-        //     if let Some(node) = r {
-        //         channel
-        //             .unbounded_send(SchedulerMsg::Suspended { node, scope })
-        //             .unwrap();
-        //     }
-        // }
-        // });
-
-        todo!()
-    }
-
     /// Store a value between renders
     ///
     /// This is *the* foundational hook for all other hooks.
@@ -452,18 +425,6 @@ impl Scope {
         self.generation.set(self.generation.get() + 1);
     }
 
-    // General strategy here is to load up the appropriate suspended task and then run it.
-    // Suspended nodes cannot be called repeatedly.
-    pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
-        let mut nodes = &mut self.items.get_mut().suspended_nodes;
-
-        if let Some(suspended) = nodes.remove(&task_id) {
-            let sus: &'a VSuspended = suspended;
-            // let mut boxed = sus.callback.borrow_mut().take().unwrap();
-            // let new_node: Element = boxed();
-        }
-    }
-
     pub fn root_node(&self) -> &VNode {
         let node = *self.wip_frame().nodes.borrow().get(0).unwrap();
         unsafe { std::mem::transmute(&*node) }
@@ -488,7 +449,7 @@ impl BumpFrame {
         Self { bump, nodes }
     }
 
-    pub fn allocated_bytes(&self) -> usize {
+    pub fn _allocated_bytes(&self) -> usize {
         self.bump.allocated_bytes()
     }
 
@@ -569,7 +530,7 @@ impl HookList {
 
     /// Get the ammount of memory a hooklist uses
     /// Used in heuristics
-    pub fn get_hook_arena_size(&self) -> usize {
+    pub fn _get_hook_arena_size(&self) -> usize {
         self.arena.allocated_bytes()
     }
 }

+ 0 - 5
packages/core/src/scopearena.rs

@@ -186,7 +186,6 @@ impl ScopeArena {
                 items: RefCell::new(SelfReferentialItems {
                     listeners: Default::default(),
                     borrowed_props: Default::default(),
-                    suspended_nodes: Default::default(),
                     tasks: Default::default(),
                 }),
             });
@@ -225,13 +224,11 @@ impl ScopeArena {
         let SelfReferentialItems {
             borrowed_props,
             listeners,
-            suspended_nodes,
             tasks,
         } = scope.items.get_mut();
 
         borrowed_props.clear();
         listeners.clear();
-        suspended_nodes.clear();
         tasks.clear();
 
         self.free_scopes.borrow_mut().push(scope);
@@ -323,13 +320,11 @@ impl ScopeArena {
             let mut items = scope.items.borrow_mut();
 
             // just forget about our suspended nodes while we're at it
-            items.suspended_nodes.clear();
             items.tasks.clear();
 
             // guarantee that we haven't screwed up - there should be no latent references anywhere
             debug_assert!(items.listeners.is_empty());
             debug_assert!(items.borrowed_props.is_empty());
-            debug_assert!(items.suspended_nodes.is_empty());
             debug_assert!(items.tasks.is_empty());
 
             // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.

+ 0 - 3
packages/core/src/virtual_dom.rs

@@ -297,7 +297,6 @@ impl VirtualDom {
                     SchedulerMsg::Immediate(s) => {
                         self.dirty_scopes.insert(s);
                     }
-                    SchedulerMsg::Suspended { scope } => todo!(),
                 }
             }
         }
@@ -548,8 +547,6 @@ pub enum SchedulerMsg {
 
     // an async task pushed from an event handler (or just spawned)
     TaskPushed(ScopeId),
-
-    Suspended { scope: ScopeId },
 }
 
 /// User Events are events that are shuttled from the renderer into the VirtualDom trhough the scheduler channel.

+ 1 - 4
packages/ssr/src/lib.rs

@@ -149,7 +149,7 @@ impl<'a> TextRenderer<'a, '_> {
                 }
                 write!(f, "{}", text.text)?
             }
-            VNode::Anchor(_anchor) => {
+            VNode::Placeholder(_anchor) => {
                 //
                 if self.cfg.indent {
                     for _ in 0..il {
@@ -252,9 +252,6 @@ impl<'a> TextRenderer<'a, '_> {
                 } else {
                 }
             }
-            VNode::Suspended { .. } => {
-                // we can't do anything with suspended nodes
-            }
         }
         Ok(())
     }

+ 0 - 4
packages/web/Cargo.toml

@@ -33,10 +33,6 @@ gloo-timers = { version = "0.2.1", features = ["futures"] }
 futures-util = "0.3.15"
 smallstr = "0.2.0"
 
-[patch.crates-io]
-wasm-bindgen = { path = "../../../Tinkering/wasm-bindgen/" }
-
-
 [dependencies.web-sys]
 version = "0.3.51"
 features = [

+ 31 - 31
src/lib.rs

@@ -32,8 +32,10 @@
 //! ```
 //! use dioxus::prelude::*;
 //!
-//! fn Example(cx: Context<()>) -> DomTree {
-//!     html! { <div> "Hello, world!" </div> }
+//! fn App(cx: Context, props: &()) -> Element {
+//!     cx.render(rsx!(
+//!         div {"hello world"}
+//!     ))
 //! }
 //! ```
 //! Components need to take a "Context" parameter which is generic over some properties. This defines how the component can be used
@@ -41,11 +43,15 @@
 //! `()`, and components with properties must declare their properties as a struct:
 //!
 //! ```
-//! #[derive(Props)]
-//! struct Props { name: String }
+//! #[derive(Props, PartialEq)]
+//! struct AppProps {
+//!     name: String
+//! }
 //!
-//! fn Example(cx: Context<Props>) -> DomTree {
-//!     html! { <div> "Hello {cx.props.name}!" </div> }
+//! fn App(cx: Context, props: &AppProps) -> Element {
+//!     cx.render(rsx!(
+//!         div { "Hello {props.name}!" }
+//!     ))
 //! }
 //! ```
 //!
@@ -57,22 +63,20 @@
 //!
 //! ```
 //! #[derive(Props)]
-//! struct Props<'a> { name: &'a str }
+//! struct Props<'a> {
+//!     name: &'a str
+//! }
 //!
-//! fn Example<'a>(cx: Context<'a, Props<'a>>) -> DomTree {
-//!     html! { <div> "Hello {cx.props.name}!" </div> }
+//! fn Example(cx: Context, props: &AppProps) -> Element {
+//!     cx.render(rsx!(
+//!         div { "Hello {props.name}!" }
+//!     ))
 //! }
 //! ```
 //!
-//!
-//!
-//! The lifetimes might look a little messy, but are crucially important for Dioxus's efficiency and overall ergonimics.
-//! Components can also be crafted as pub static closures, enabling type inference without all the type signature noise. However,
-//! closure-style components cannot work with borrowed data due to limitations in Rust's lifetime system.
-//!
 //! To use custom properties for components, you'll need to derive the `Props` trait for your properties. This trait
-//! exposes a compile-time correct builder pattern (similar to typed-builder) that can be used in the `rsx!` and `html!`
-//! macros to build components. Component props may have default fields notated by the `Default` attribute:
+//! exposes a compile-time correct builder pattern (similar to typed-builder) that can be used in the `rsx!` macro to
+//! build components. Component props may have default fields notated by the `Default` attribute:
 //!
 //! ```
 //! #[derive(Props)]
@@ -82,7 +86,7 @@
 //!     #[props(default = false)]
 //!     checked: bool,
 //!
-//!     #[props(default, setter(strip_option, into))]
+//!     #[props(default, into))]
 //!     title: Option<String>
 //! }
 //! ```
@@ -94,34 +98,30 @@
 //!
 //! ```
 //! pub pub static Example: FC<()> = |cx, props|{
-//!     let (val, set_val) = use_state(cx, || 0);
+//!     let mut val = use_state(cx, || 0);
 //!     cx.render(rsx!(
-//!         button { onclick: move |_| set_val(val + 1) }
+//!         button { onclick: move |_| val += 1 }
 //!     ))
 //! }
 //! ````
 //!
-//! Instead of using a single struct to represent a component and its state, hooks use the "use_hook" building block
-//! which allows the persistence of data between function component renders. This primitive is exposed directly through
-//! the `Context` item:
+//! As a building block for hooks, Dioxus provides the `use_hook` method on Context that stores a provided value in a
+//! list of other values. Whenever `use_hook` is called, the next hook value in the list is returned.
 //! ```
-//! fn my_hook<'a>(cx: &impl Scoped<'a>) -> &'a String {
+//! fn my_hook(cx: Context) -> &String {
 //!     cx.use_hook(
 //!         // Initializer stores a value
 //!         |hook_idx| String::new("stored_data"),
 //!
 //!         // Runner returns the hook value every time the component is rendered
 //!         |hook| &*hook,
-//!
-//!         // Cleanup runs after the component is unmounted
-//!         |hook| log::debug!("cleaning up hook with value {:#?}", hook)
 //!     )
 //! }
 //! ```
-//! Under the hood, hooks store their data in a series of "memory cells". The first render defines the layout of these
-//! memory cells, and on each subsequent render, each `use_hook` call accesses its corresponding memory cell. If a hook
-//! accesses the wrong memory cell, `use_hook` will panic, and your app will crash. You can always use `try_use_hook` but
-//! these types of errors can be easily mitigated by following the rules of hooks:
+//! Under the hood, hooks store their data in a list of `Box<dyn Any>`. The first render defines the layout of these
+//! list, and on each subsequent render, each `use_hook` call accesses its corresponding list item. If a hook
+//! accesses the wrong index, `use_hook` will panic when trying to downcast `Any` to your type, and your app will crash.
+//! These types of errors can be easily mitigated by following the rules of hooks:
 //!
 //! - Don’t call Hooks inside loops, conditions, or nested functions
 //! - Don't call hooks in changing order between renders