diff --git a/Cargo.toml b/Cargo.toml
index 7b8d3e7c3..7a0ae8b11 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,7 @@ split-debuginfo = "unpacked"
[dev-dependencies]
futures = "0.3.15"
+log = "0.4.14"
# For the tide ssr examples
# async-std = { version="1.9.0", features=["attributes"] }
# tide = { version="0.16.0" }
diff --git a/examples/README.md b/examples/README.md
index 69e03b338..8b8cd0911 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,29 +1,50 @@
# Examples
-Most of these examples are run through webview so you don't need the dioxus cli installed to preview the functionality. Anything labeled `_web` will need to be built with the Dioxus CLI to preview features that only a native bundle can handle.
+Most of these examples are run through webview so you don't need the dioxus cli installed to preview the functionality.
-List of examples:
+These examples are fully-fledged micro apps. They can be ran with the `cargo run --example XYZ`
-| Example | What it does |
-| --------------------------------------- | ------------ |
-| [The basics](./basics.rs) | this does |
-| [fine grained reactivity](./signals.rs) | this does |
-| Global State Management | this does |
-| Virtual Refs | this does |
-| Inline Styles | this does |
-| Conditional Rendering | this does |
-| Maps/Iterators | this does |
-| Render To string | this does |
-| Component Children | this does |
-| Function Driven children | this does |
-| Memoization | this does |
-| Borrowed Data | this does |
-| Fragments | this does |
-| Null/None Components | this does |
-| Spread Pattern for props | this does |
-| Controlled Inputs | this does |
-| Custom Elements | this does |
-| Testing And debugging | this does |
-| Asynchronous Data | this does |
-| Fiber/Scheduled Rendering | this does |
-| CSS Compiled Styles | this does |
+| Example | What it does | Status |
+| --------------------------------------------------- | ------------------------------------------- | ------ |
+| [The basics](./basics.rs) | A few basic examples to preview Dioxus | 🛠|
+| [fine grained reactivity](./signals.rs) | Escape `diffing` by writing values directly | 🛠|
+| [Global State Management](./statemanagement.rs) | Share state between components | 🛠|
+| [Virtual Refs]() | Cross-platform imperative elements | 🛠|
+| [Inline Styles](./inline-styles.rs) | Define styles for elements inline | 🛠|
+| [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals | ✅ |
+
+These examples are not necessarily meant to be run, but rather serve as a reference for the given functionality.
+
+| Example | What it does | Status |
+| --------------------------------------------------- | ----------------------------------------------- | ------ |
+| [The basics](./basics.rs) | A few basic examples to preview Dioxus | 🛠|
+| [fine grained reactivity](./signals.rs) | Escape `diffing` by writing values directly | 🛠|
+| [Global State Management](./statemanagement.rs) | Share state between components | 🛠|
+| [Virtual Refs]() | Cross-platform imperative elements | 🛠|
+| [Inline Styles](./inline-styles.rs) | Define styles for elements inline | 🛠|
+| [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals | ✅ |
+| [Maps/Iterators](./iterators.rs) | Use iterators in the rsx! macro | 🛠|
+| [Render To string](./tostring.rs) | Render a mounted virtualdom to a string | 🛠|
+| [Component Children](./children.rs) | Pass children into child components | 🛠|
+| [Function Driven children]() | Pass functions to make VNodes | 🛠|
+| [Memoization & Borrowed Data](./memo.rs) | Suppress renders, borrow from parents | ✅ |
+| [Fragments](./fragments.rs) | Support root-less element groups | ✅ |
+| [Null/None Components](./empty.rs) | Return nothing! | 🛠|
+| [Spread Pattern for props](./spreadpattern.rs) | Manually specify and override props | ✅ |
+| [Controlled Inputs](./controlled-inputs.rs) | this does | 🛠|
+| [Custom Elements]() | Define custom elements | 🛠|
+| [Web Components]() | Custom elements to interface with WebComponents | 🛠|
+| [Testing And debugging]() | this does | 🛠|
+| [Asynchronous Data]() | Using suspense to wait for data | 🛠|
+| [Fiber/Scheduled Rendering]() | this does | 🛠|
+| [CSS Compiled Styles]() | this does | 🛠|
+| [Anti-patterns](./antipatterns.rs) | A collection of discouraged patterns | ✅ |
+| [Complete rsx reference](./rsx_usage.rs) | A complete reference for all rsx! usage | ✅ |
+| [Event Listeners](./listener.rs) | Attach closures to events on elements | ✅ |
+
+These web-specific examples must be run with `dioxus-cli` using `dioxus develop --example XYZ`
+
+| Example | What it does |
+| ------- | ------------ |
+| asd | this does |
+| asd | this does |
diff --git a/examples/listener.rs b/examples/listener.rs
deleted file mode 100644
index 0e22f8666..000000000
--- a/examples/listener.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-#![allow(unused, non_upper_case_globals)]
-
-use dioxus_core::prelude::*;
-
-fn main() {}
-
-static Example: FC<()> = |cx| {
- let (name, set_name) = use_state(&cx, || "...?");
-
- cx.render(rsx!(
- div {
- h1 { "Hello, {name}" }
- // look ma - we can rsx! and html! together
- {["jack", "jill"].iter().map(|f| html!())}
- }
- ))
-};
-
-pub fn render<'src, 'a, F: for<'b> FnOnce(&'b NodeFactory<'src>) -> VNode<'src> + 'src + 'a, P>(
- cx: &'a Context<'src, P>,
- lazy_nodes: LazyNodes<'src, F>,
-) -> VNode<'src> {
- cx.render(lazy_nodes)
-}
diff --git a/examples/antipatterns.rs b/examples/reference/antipatterns.rs
similarity index 97%
rename from examples/antipatterns.rs
rename to examples/reference/antipatterns.rs
index e562c093b..b07cf22a0 100644
--- a/examples/antipatterns.rs
+++ b/examples/reference/antipatterns.rs
@@ -21,10 +21,10 @@ fn main() {}
/// Antipattern: Iterators without keys
/// -----------------------------------
///
-/// This is considered an anti-pattern for performance reasons. Dioxus must diff your current and old layout and must
+/// This is considered an anti-pattern for performance reasons. Dioxus will diff your current and old layout and must
/// take a slower path if it can't correlate old elements with new elements. Lists are particularly susceptible to the
/// "slow" path, so you're strongly encouraged to provide a unique ID stable between renders. Additionally, providing
-/// the *wrong* keys is even worse. Keys should be:
+/// the *wrong* keys is even worse - props might be assigned to the wrong components! Keys should be:
/// - Unique
/// - Stable
/// - Predictable
@@ -39,7 +39,7 @@ static AntipatternNoKeys: FC = |cx| {
rsx!(in cx, ul {
{cx.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
});
- // Like this:
+ // RIGHT: Like this:
rsx!(in cx, ul {
{cx.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
})
diff --git a/examples/reference/async.rs b/examples/reference/async.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/async.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/basics.rs b/examples/reference/basics.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/basics.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/children.rs b/examples/reference/children.rs
new file mode 100644
index 000000000..38aee99f1
--- /dev/null
+++ b/examples/reference/children.rs
@@ -0,0 +1,45 @@
+//! Example: Children
+//! -----------------
+//!
+//! Dioxus supports passing children in from the parent. These children are allocated in the parent and just forced
+//! into the child. Components that pass in children may not be safely memoized, though in practice its rare for a
+//! change in a parent to not result in a different set of children.
+//!
+//! In Dioxus, children can *only be a list*. Unlike React, you cannot pass in functions or arbitrary data. This is
+//! partially a limitaiton in having static types, but is rather intentional to encourage the use of attributes where
+//! arbitrary child data might normally be used. Check out the `function driven children` example for how to adopt your
+//! React pattern for Dioxus' semantics.
+//!
+//! Dioxus will let you use the `children` method more than once - and it's semantically *okay* - but you'll likely
+//! ruin your page if you try to clone elements in this way. Under the hood, Dioxus shares a "mounted ID" for each node,
+//! and mounting the same VNode in two places will overwrite the first mounted ID. This will likely lead to dead elements.
+//!
+//! In the future, this might become a runtime error, so consider it an error today.
+
+use dioxus::prelude::*;
+fn main() {}
+
+static App: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+ Banner {
+ p { "Some Content1" }
+ }
+ Banner {
+ p { "Some Content2" }
+ }
+ }
+ })
+};
+
+static Banner: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+ h1 { "This is a great banner!" }
+ div { class: "content"
+ {cx.children()}
+ }
+ footer { "Wow, what a great footer" }
+ }
+ })
+};
diff --git a/examples/reference/conditional_rendering.rs b/examples/reference/conditional_rendering.rs
new file mode 100644
index 000000000..3048c0108
--- /dev/null
+++ b/examples/reference/conditional_rendering.rs
@@ -0,0 +1,92 @@
+//! Example: Conditional Rendering
+//! ------------------------------
+//!
+//! This example shows how to hide or show elements using conditional rendering.
+//!
+//! Often times you might want to display a different UI given some sort of condition. This is called "conditonal rendering".
+//! In Dioxus, you can perform conditional rendering with optionals or matching.
+//!
+//! The rsx! and html! macro accepts anything that is `IntoIter`. Options and Results both implement
+//! IntoIter for impl VNode, so you can use option/result for conditional rendering.
+
+use dioxus::prelude::*;
+
+fn main() {}
+
+// Convert a boolean conditional into a hide/show
+#[derive(PartialEq, Props)]
+struct MyProps {
+ should_show: bool,
+}
+static Example: FC = |cx| {
+ cx.render(rsx! {
+ div {
+ {cx.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
+// must render it.
+//
+// Dioxus will let you render any `LazyNodes` into a `VNode` with `cx.render`. `rsx!` also supports the `in cx` syntax
+// which will do essentially the same thing as `cx.render`.
+//
+// In short:
+// `rsx!(in cx, ...)` is shorthand for `cx.render(rsx!(...))`
+#[derive(PartialEq, Props)]
+struct MyProps1 {
+ should_show: bool,
+}
+static Example1: FC = |cx| {
+ cx.render(rsx! {
+ div {
+ // With matching
+ {match cx.should_show {
+ true => cx.render(rsx!(div {"it is true!"})),
+ false => rsx!(in cx, div {"it is false!"}),
+ }}
+
+ // or with just regular conditions
+ {if cx.should_show {
+ rsx!(in cx, div {"it is true!"})
+ } else {
+ rsx!(in cx, div {"it is false!"})
+ }}
+
+ // or with optional chaining
+ {
+ cx.should_show
+ .then(|| rsx!(in cx, div {"it is false!"}))
+ .unwrap_or_else(|| rsx!(in cx, div {"it is false!"}))
+ }
+ }
+ })
+};
+
+/// Matching can be expanded
+
+#[derive(PartialEq)]
+enum Color {
+ Green,
+ Yellow,
+ Red,
+}
+#[derive(PartialEq, Props)]
+struct MyProps2 {
+ color: Color,
+}
+static Example2: FC = |cx| {
+ cx.render(rsx! {
+ div {
+ {match cx.color {
+ Color::Green => rsx!(in cx, div {"it is Green!"}),
+ Color::Yellow => rsx!(in cx, div {"it is Yellow!"}),
+ Color::Red => rsx!(in cx, div {"it is Red!"}),
+ }}
+ }
+ })
+};
diff --git a/examples/reference/controlled_inputs.rs b/examples/reference/controlled_inputs.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/controlled_inputs.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/custom_elements.rs b/examples/reference/custom_elements.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/custom_elements.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/empty.rs b/examples/reference/empty.rs
new file mode 100644
index 000000000..95ef10506
--- /dev/null
+++ b/examples/reference/empty.rs
@@ -0,0 +1,15 @@
+//! Example: Null/None Children
+//! ---------------------------
+//!
+//! This is a simple pattern that allows you to return no elements!
+
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/fiber.rs b/examples/reference/fiber.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/fiber.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/fragments.rs b/examples/reference/fragments.rs
new file mode 100644
index 000000000..848c5f4e8
--- /dev/null
+++ b/examples/reference/fragments.rs
@@ -0,0 +1,45 @@
+//! Example: Fragments
+//! ------------------
+//!
+//! Dioxus can return multiple elements without a container through the use of the VNode called a "Fragment". Fragments do not
+//! have a mounted root and are inserted inline with their siblings. There are three ways of creating fragments as outlined
+//! in the examples below:
+//! - By returning multiple elements in Rsx!
+//! - By using the `Fragment` component
+//! - By using the fragment() method on the node factory
+
+use dioxus::prelude::*;
+fn main() {}
+
+// Returning multiple elements with rsx! or html!
+static Example1: FC<()> = |cx| {
+ cx.render(rsx! {
+ h1 { }
+ h2 { }
+ h3 { }
+ // {}
+ // "hello world"
+ })
+};
+
+// Using the Fragment component
+static Example2: FC<()> = |cx| {
+ cx.render(rsx! {
+ Fragment {
+ div {}
+ div {}
+ "asd"
+ }
+ })
+};
+
+// Using the `fragment` method on the NodeFactory
+static Example3: FC<()> = |cx| {
+ cx.render(LazyNodes::new(move |fac: &NodeFactory| {
+ fac.fragment_builder(None, |list| {
+ list.add_child(fac.text(format_args!("A")))
+ .add_child(fac.text(format_args!("B")))
+ .finish()
+ })
+ }))
+};
diff --git a/examples/reference/global_css.rs b/examples/reference/global_css.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/global_css.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/inline_styles.rs b/examples/reference/inline_styles.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/inline_styles.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/iterators.rs b/examples/reference/iterators.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/iterators.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/listener.rs b/examples/reference/listener.rs
new file mode 100644
index 000000000..10a32cde1
--- /dev/null
+++ b/examples/reference/listener.rs
@@ -0,0 +1,50 @@
+//! Example: Listeners
+//! ------------------
+//!
+//! This example demonstrates the various ways listeners may be used in Dioxus.
+//! Listeners may be at most `FnMut` - but not `FnOnce`.
+//! Listeners may borrow the lifetime of cx (children and hooks), but not of local (stack) data.
+
+use dioxus::prelude::*;
+
+fn main() {}
+
+/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
+static ButtonList: FC<()> = |cx| {
+ let (name, set_name) = use_state(&cx, || "...?");
+
+ let names = ["jack", "jill", "john", "jane"]
+ .iter()
+ .map(move |n| rsx!(button { onclick: move |_| set_name(n), "{n}" }));
+
+ cx.render(rsx!(
+ div {
+ h1 { "Hello, {name}" }
+ {names}
+ }
+ ))
+};
+
+/// This shows how listeners may be without a visible change in the display.
+/// Check the console.
+static NonUpdatingEvents: FC<()> = |cx| {
+ rsx!(in cx, div {
+ button {
+ onclick: move |_| log::info!("Did not cause any updates!")
+ "Click me to log!"
+ }
+ })
+};
+
+static DisablePropogation: FC<()> = |cx| {
+ rsx!(in cx,
+ div {
+ onclick: move |_| log::info!("event propogated to the div!")
+ button {
+ onclick: move |evt| {
+ log::info!("Button will allow propogation");
+ }
+ }
+ }
+ )
+};
diff --git a/examples/reference/memo.rs b/examples/reference/memo.rs
new file mode 100644
index 000000000..b8cb648a1
--- /dev/null
+++ b/examples/reference/memo.rs
@@ -0,0 +1,85 @@
+//! Example: Memoization
+//! --------------------
+//!
+//! This example showcases how memoization works in Dioxus.
+//!
+//! Memoization is the process in which Dioxus skips diffing child components if their props don't change.
+//! In React, components are never memoized unless wrapped in `memo` or configured with `shouldComponentUpdate`.
+//!
+//! Due to the safety guarantees of Rust, we can automatically memoize components in some circumstances. Whenever a
+//! component's properties are valid for the `'static` lifetime, Dioxus will automatically compare the props before
+//! diffing the component. If the props don't change (according to PartialEq), the component will not be re-rendered.
+//!
+//! However, if the props use some generics or borrow from their parent, then Dioxus can't safely supress updates,
+//! and is forced to render the child. If you think that this behavior is wrong for your usecase, you can implement
+//! the memo method yourself, but beware, doing so is UNSAFE and may cause issues if you do it wrong.
+//!
+//! If you want to gain that little bit extra performance, consider using global state management, signals, or
+//! memoized collections like im-rc which are designed for this use case.
+
+use dioxus::prelude::*;
+fn main() {}
+
+// By default, components with no props are always memoized.
+// A props of () is considered empty.
+static Example: FC<()> = |cx| {
+ 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.
+// Notice how these props implement PartialEq - this is required for 'static props
+#[derive(PartialEq, Props)]
+struct MyProps1 {
+ name: String,
+}
+
+static Example1: FC = |cx| {
+ cx.render(rsx! {
+ div { "100% memoized! {cx.name}" }
+ })
+};
+
+// These props do not borrow any content, and therefore can be safely memoized.
+// In contrast with the `String` example, these props use `Rc` which operates similar to strings in JavaScript.
+// These strings cannot be modified, but may be cheaply shared in many places without issue.
+#[derive(PartialEq, Props)]
+struct MyProps2 {
+ name: std::rc::Rc,
+}
+
+static Example2: FC = |cx| {
+ cx.render(rsx! {
+ div { "100% memoized! {cx.name}" }
+ })
+};
+
+// These props *do* borrow any content, and therefore cannot be safely memoized!.
+#[derive(PartialEq, Props)]
+struct MyProps3<'a> {
+ name: &'a str,
+}
+// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
+// Using the `static Example: FC<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might
+// not exactly be what you want
+fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode {
+ cx.render(rsx! {
+ div { "Not memoized! {cx.name}" }
+ })
+}
+
+// These props *do* borrow any content, and therefore cannot be safely memoized!.
+// However, they cannot be compared, so we don't need the PartialEq flag.
+#[derive(Props)]
+struct MyProps4<'a> {
+ onhandle: &'a dyn Fn(),
+}
+
+// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
+fn Example4<'a>(cx: Context<'a, MyProps4<'a>>) -> VNode {
+ cx.render(rsx! {
+ div { "Not memoized!", onclick: move |_| (cx.onhandle)() }
+ })
+}
diff --git a/examples/reference/mod.rs b/examples/reference/mod.rs
new file mode 100644
index 000000000..f08cae033
--- /dev/null
+++ b/examples/reference/mod.rs
@@ -0,0 +1,2 @@
+mod iterator;
+mod memo;
diff --git a/examples/reference/noderefs.rs b/examples/reference/noderefs.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/noderefs.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/signals.rs b/examples/reference/signals.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/signals.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/spreadpattern.rs b/examples/reference/spreadpattern.rs
new file mode 100644
index 000000000..4810e3af8
--- /dev/null
+++ b/examples/reference/spreadpattern.rs
@@ -0,0 +1,40 @@
+//! Example: Spread pattern for Components
+//! --------------------------------------
+//!
+//! Dioxus supports the "spread" pattern for manually building a components properties. This is useful when props
+//! are passed down from a parent, or it's more ergonomic to construct props from outside the rsx! macro.
+//!
+//! To use the spread pattern, simply pass ".." followed by a Rust epxression. This pattern also supports overriding
+//! values, using the manual props as the base and then modifying fields specified with non-spread attributes.
+
+use dioxus::prelude::*;
+fn main() {}
+
+static App: FC<()> = |cx| {
+ let props = MyProps {
+ count: 0,
+ live: true,
+ name: "Dioxus",
+ };
+ cx.render(rsx! {
+ Example { ..props, count: 10, div {"child"} }
+ })
+};
+
+#[derive(PartialEq, Props)]
+struct MyProps {
+ count: u32,
+ live: bool,
+ name: &'static str,
+}
+
+static Example: FC = |cx| {
+ cx.render(rsx! {
+ div {
+ h1 { "Hello, {cx.name}"}
+ h3 {"Are we alive? {cx.live}"}
+ p {"Count is {cx.count}"}
+ { cx.children() }
+ }
+ })
+};
diff --git a/examples/reference/statemanagement.rs b/examples/reference/statemanagement.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/statemanagement.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/suspense.rs b/examples/reference/suspense.rs
similarity index 100%
rename from examples/suspense.rs
rename to examples/reference/suspense.rs
diff --git a/examples/reference/testing.rs b/examples/reference/testing.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/testing.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/tostring.rs b/examples/reference/tostring.rs
new file mode 100644
index 000000000..096f0d9e0
--- /dev/null
+++ b/examples/reference/tostring.rs
@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+};
diff --git a/examples/reference/webcomponents.rs b/examples/reference/webcomponents.rs
new file mode 100644
index 000000000..c27f8ff69
--- /dev/null
+++ b/examples/reference/webcomponents.rs
@@ -0,0 +1,21 @@
+//! Example: Web Components
+//! -----------------------
+//!
+//! Web components are a flavor of html elements that can be user-defined.
+//! See: https://www.webcomponents.org for more information.
+//!
+//! Users who use webcomponents typically don't use Dioxus. However, if you would like to use webcomponents in Dioxus,
+//! you can easily create a new custom element with compile-time correct wrappers around the webcomponent.
+//!
+//! We don't support building new webcomponents with Dioxus, however.
+//!
+
+use dioxus::{builder::ElementBuilder, prelude::NodeFactory};
+
+fn main() {}
+
+// TODO
+struct MyEle<'a, 'b> {
+ el: ElementBuilder<'a, 'b>,
+ fac: &'b NodeFactory<'a>,
+}
diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs
index 27614fc56..f5c811463 100644
--- a/examples/rsx_usage.rs
+++ b/examples/rsx_usage.rs
@@ -120,7 +120,7 @@ static Example: FC<()> = |cx| {
{if true {
rsx!(in cx, h1 {"Top text"})
} else {
- cx.render(rsx!( h1 {"Bottom text"}))
+ rsx!(in cx, h1 {"Bottom text"})
}}
// returning "None" is a bit noisy... but rare in practice
@@ -168,7 +168,13 @@ static Example: FC<()> = |cx| {
rsx!(Taller { ..props })
}}
- // Can take children
+ // Spreading can also be overridden manually
+ Taller {
+ ..TallerProps { a: "ballin!" }
+ a: "not ballin!"
+ }
+
+ // Can take children too!
Taller { a: "asd", div {"hello world!"} }
}
})
diff --git a/examples/showcase.rs b/examples/showcase.rs
index 3d128893e..7fb2db260 100644
--- a/examples/showcase.rs
+++ b/examples/showcase.rs
@@ -1,26 +1,31 @@
-//! A Showcase of all the useful examples
-//!
-//!
+//! Example: Reference Showcase
+//! ---------------------------
//!
+//! This example provides a cute interface for browsing the reference examples.
-fn main() {
- use_css_consumer(&cx, "mystyle");
-
- // at the global head of the app
- use_css_provider(&cx, |cfg| {});
- use_recoil_provider(&cx, |cfg| {});
-
- let recoil = use_recoil_api(&cx, |_| {});
- use_websocket_connection(&cx, move |cfg| {
- cfg.on_receive(move |event| match event.data::() {
- Ok(msg) => match msg {
- a => recoil.set(&ATOM, 10),
- c => recoil.set(&ATOM, 20),
- _ => {}
- },
- Err(e) => {}
- });
- cfg.on_close(move |event| {});
- cfg.on_open(move |event| {});
- });
+mod reference {
+ mod antipatterns;
+ mod basics;
+ mod children;
+ mod conditional_rendering;
+ mod controlled_inputs;
+ mod custom_elements;
+ mod empty;
+ mod fiber;
+ mod fragments;
+ mod global_css;
+ mod inline_styles;
+ mod iterators;
+ mod listener;
+ mod memo;
+ mod noderefs;
+ mod signals;
+ mod spreadpattern;
+ mod statemanagement;
+ mod suspense;
+ mod testing;
+ mod tostring;
+ mod webcomponents;
}
+
+fn main() {}
diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs
index 068a0bb86..24fc74d8e 100644
--- a/packages/core-macro/src/rsx/component.rs
+++ b/packages/core-macro/src/rsx/component.rs
@@ -80,33 +80,46 @@ impl ToTokens for Component {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let name = &self.name;
- let using_manual_override = self.manual_props.is_some();
-
- let mut builder = {
- match &self.manual_props {
- Some(manual_props) => quote! { #manual_props },
- None => quote! { fc_to_builder(#name) },
- }
- };
-
let mut has_key = None;
- for field in &self.body {
- if field.name.to_string() == "key" {
- has_key = Some(field);
- } else {
- match using_manual_override {
- true => panic!("Currently we don't support manual props and prop fields. Choose either manual props or prop fields"),
- false => builder.append_all(quote! {#field}),
+ let builder = match &self.manual_props {
+ Some(manual_props) => {
+ let mut toks = quote! {
+ let mut __manual_props = #manual_props;
+ };
+ for field in &self.body {
+ if field.name.to_string() == "key" {
+ has_key = Some(field);
+ } else {
+ let name = &field.name;
+ let val = &field.content;
+ toks.append_all(quote! {
+ __manual_props.#name = #val;
+ });
+ }
}
+ toks.append_all(quote! {
+ __manual_props
+ });
+ quote! {{
+ #toks
+ }}
}
- }
-
- if !using_manual_override {
- builder.append_all(quote! {
- .build()
- });
- }
+ None => {
+ let mut toks = quote! { fc_to_builder(#name) };
+ for field in &self.body {
+ if field.name.to_string() == "key" {
+ has_key = Some(field);
+ } else {
+ toks.append_all(quote! {#field})
+ }
+ }
+ toks.append_all(quote! {
+ .build()
+ });
+ toks
+ }
+ };
let key_token = match has_key {
Some(field) => {
diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs
index 931a53d9e..4002c183d 100644
--- a/packages/core/src/events.rs
+++ b/packages/core/src/events.rs
@@ -4,7 +4,7 @@
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
-use std::rc::Rc;
+use std::{ops::Deref, rc::Rc};
use crate::{innerlude::ScopeIdx, virtual_dom::RealDomNode};
@@ -28,21 +28,21 @@ impl EventTrigger {
#[derive(Debug)]
pub enum VirtualEvent {
// Real events
- ClipboardEvent(Rc),
- CompositionEvent(Rc),
- KeyboardEvent(Rc),
- FocusEvent(Rc),
- FormEvent(Rc),
- SelectionEvent(Rc),
- TouchEvent(Rc),
- UIEvent(Rc),
- WheelEvent(Rc),
- MediaEvent(Rc),
- AnimationEvent(Rc),
- TransitionEvent(Rc),
- ToggleEvent(Rc),
- MouseEvent(Rc),
- PointerEvent(Rc),
+ ClipboardEvent(on::ClipboardEvent),
+ CompositionEvent(on::CompositionEvent),
+ KeyboardEvent(on::KeyboardEvent),
+ FocusEvent(on::FocusEvent),
+ FormEvent(on::FormEvent),
+ SelectionEvent(on::SelectionEvent),
+ TouchEvent(on::TouchEvent),
+ UIEvent(on::UIEvent),
+ WheelEvent(on::WheelEvent),
+ MediaEvent(on::MediaEvent),
+ AnimationEvent(on::AnimationEvent),
+ TransitionEvent(on::TransitionEvent),
+ ToggleEvent(on::ToggleEvent),
+ MouseEvent(on::MouseEvent),
+ PointerEvent(on::PointerEvent),
// Whenever a task is ready (complete) Dioxus produces this "FiberEvent"
FiberEvent { task_id: u16 },
@@ -75,12 +75,21 @@ pub mod on {
use super::VirtualEvent;
macro_rules! event_directory {
- ( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => {
+ ( $( $eventdata:ident($wrapper:ident): [ $( $name:ident )* ]; )* ) => {
$(
+ #[derive(Debug)]
+ pub struct $wrapper(Rc);
+ impl Deref for $wrapper {
+ type Target = Rc;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
$(
pub fn $name<'a>(
c: &'_ NodeFactory<'a>,
- callback: impl Fn(Rc) + 'a,
+ callback: impl Fn($wrapper) + 'a,
) -> Listener<'a> {
let bump = &c.bump();
Listener {
@@ -88,7 +97,7 @@ pub mod on {
mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
scope: c.scope_ref.arena_idx,
callback: bump.alloc(move |evt: VirtualEvent| match evt {
- VirtualEvent::$eventdata(event) => callback(event),
+ VirtualEvent::$wrapper(event) => callback(event),
_ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
}),
}
@@ -99,36 +108,36 @@ pub mod on {
}
event_directory! {
- ClipboardEvent: [copy cut paste];
- CompositionEvent: [compositionend compositionstart compositionupdate];
- KeyboardEvent: [keydown keypress keyup];
- FocusEvent: [focus blur];
- FormEvent: [change input invalid reset submit];
- MouseEvent: [
+ ClipboardEventInner(ClipboardEvent): [copy cut paste];
+ CompositionEventInner(CompositionEvent): [compositionend compositionstart compositionupdate];
+ KeyboardEventInner(KeyboardEvent): [keydown keypress keyup];
+ FocusEventInner(FocusEvent): [focus blur];
+ FormEventInner(FormEvent): [change input invalid reset submit];
+ MouseEventInner(MouseEvent): [
click contextmenu doubleclick drag dragend dragenter dragexit
dragleave dragover dragstart drop mousedown mouseenter mouseleave
mousemove mouseout mouseover mouseup
];
- PointerEvent: [
+ PointerEventInner(PointerEvent): [
pointerdown pointermove pointerup pointercancel gotpointercapture
lostpointercapture pointerenter pointerleave pointerover pointerout
];
- SelectionEvent: [select];
- TouchEvent: [touchcancel touchend touchmove touchstart];
- UIEvent: [scroll];
- WheelEvent: [wheel];
- MediaEvent: [
+ SelectionEventInner(SelectionEvent): [select];
+ TouchEventInner(TouchEvent): [touchcancel touchend touchmove touchstart];
+ UIEventInner(UIEvent): [scroll];
+ WheelEventInner(WheelEvent): [wheel];
+ MediaEventInner(MediaEvent): [
abort canplay canplaythrough durationchange emptied encrypted
ended error loadeddata loadedmetadata loadstart pause play
playing progress ratechange seeked seeking stalled suspend
timeupdate volumechange waiting
];
- AnimationEvent: [animationstart animationend animationiteration];
- TransitionEvent: [transitionend];
- ToggleEvent: [toggle];
+ AnimationEventInner(AnimationEvent): [animationstart animationend animationiteration];
+ TransitionEventInner(TransitionEvent): [transitionend];
+ ToggleEventInner(ToggleEvent): [toggle];
}
- trait GenericEvent {
+ pub trait GenericEventInner {
/// Returns whether or not a specific event is a bubbling event
fn bubbles(&self) -> bool;
/// Sets or returns whether the event should propagate up the hierarchy or not
@@ -159,15 +168,15 @@ pub mod on {
fn time_stamp(&self) -> usize;
}
- pub trait ClipboardEvent: Debug {
+ pub trait ClipboardEventInner: Debug + GenericEventInner {
// DOMDataTransfer clipboardData
}
- pub trait CompositionEvent: Debug {
+ pub trait CompositionEventInner: Debug {
fn data(&self) -> String;
}
- pub trait KeyboardEvent: Debug {
+ pub trait KeyboardEventInner: Debug {
fn char_code(&self) -> usize;
fn ctrl_key(&self) -> bool;
fn key(&self) -> String;
@@ -181,15 +190,15 @@ pub mod on {
fn get_modifier_state(&self, key_code: usize) -> bool;
}
- pub trait FocusEvent: Debug {
- /* DOMEventTarget relatedTarget */
+ pub trait FocusEventInner: Debug {
+ /* DOMEventInnerTarget relatedTarget */
}
- pub trait FormEvent: Debug {
+ pub trait FormEventInner: Debug {
fn value(&self) -> String;
}
- pub trait MouseEvent: Debug {
+ pub trait MouseEventInner: Debug {
fn alt_key(&self) -> bool;
fn button(&self) -> i16;
fn buttons(&self) -> u16;
@@ -205,7 +214,7 @@ pub mod on {
fn get_modifier_state(&self, key_code: &str) -> bool;
}
- pub trait PointerEvent: Debug {
+ pub trait PointerEventInner: Debug {
// Mouse only
fn alt_key(&self) -> bool;
fn button(&self) -> usize;
@@ -232,9 +241,9 @@ pub mod on {
fn is_primary(&self) -> bool;
}
- pub trait SelectionEvent: Debug {}
+ pub trait SelectionEventInner: Debug {}
- pub trait TouchEvent: Debug {
+ pub trait TouchEventInner: Debug {
fn alt_key(&self) -> bool;
fn ctrl_key(&self) -> bool;
fn meta_key(&self) -> bool;
@@ -245,37 +254,37 @@ pub mod on {
// touches: DOMTouchList,
}
- pub trait UIEvent: Debug {
+ pub trait UIEventInner: Debug {
// DOMAbstractView view
fn detail(&self) -> i32;
}
- pub trait WheelEvent: Debug {
+ pub trait WheelEventInner: Debug {
fn delta_mode(&self) -> i32;
fn delta_x(&self) -> i32;
fn delta_y(&self) -> i32;
fn delta_z(&self) -> i32;
}
- pub trait MediaEvent: Debug {}
+ pub trait MediaEventInner: Debug {}
- pub trait ImageEvent: Debug {
+ pub trait ImageEventInner: Debug {
// load error
}
- pub trait AnimationEvent: Debug {
+ pub trait AnimationEventInner: Debug {
fn animation_name(&self) -> String;
fn pseudo_element(&self) -> String;
fn elapsed_time(&self) -> f32;
}
- pub trait TransitionEvent: Debug {
+ pub trait TransitionEventInner: Debug {
fn property_name(&self) -> String;
fn pseudo_element(&self) -> String;
fn elapsed_time(&self) -> f32;
}
- pub trait ToggleEvent: Debug {}
+ pub trait ToggleEventInner: Debug {}
}
mod tests {
diff --git a/packages/core/src/nodebuilder.rs b/packages/core/src/nodebuilder.rs
index dc467ba4f..619d860d0 100644
--- a/packages/core/src/nodebuilder.rs
+++ b/packages/core/src/nodebuilder.rs
@@ -768,6 +768,25 @@ impl<'a> NodeFactory<'a> {
> {
ElementBuilder::new(self, tag_name)
}
+
+ pub fn child_list(&self) -> ChildrenList {
+ ChildrenList::new(&self)
+ }
+
+ pub fn fragment_builder<'b>(
+ &'b self,
+ key: Option<&'a str>,
+ builder: impl FnOnce(ChildrenList<'a, 'b>) -> &'a [VNode<'a>],
+ ) -> VNode<'a> {
+ self.fragment(builder(ChildrenList::new(&self)), key)
+ }
+
+ pub fn fragment(&self, children: &'a [VNode<'a>], key: Option<&'a str>) -> VNode<'a> {
+ VNode::Fragment(self.bump().alloc(VFragment {
+ children,
+ key: NodeKey::new_opt(key),
+ }))
+ }
}
use std::fmt::Debug;
diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs
index 411fa8a1d..a896236ea 100644
--- a/packages/core/src/nodes.rs
+++ b/packages/core/src/nodes.rs
@@ -297,6 +297,11 @@ impl<'a> NodeKey<'a> {
pub fn new(key: &'a str) -> Self {
NodeKey(Some(key))
}
+
+ #[inline]
+ pub fn new_opt(key: Option<&'a str>) -> Self {
+ NodeKey(key)
+ }
}
// ==============================