فهرست منبع

wip: docs

Worked a bit on adding more examples. Trying out a new "antipattern" example to show how *not* to use Dioxus.
Jonathan Kelley 4 سال پیش
والد
کامیت
f5683a2346

+ 62 - 0
examples/antipatterns.rs

@@ -0,0 +1,62 @@
+//! Example: Antipatterns
+//! ---------------------
+//!
+//! This example shows what *not* to do and provides a reason why a given pattern is considered an "AntiPattern". Most
+//! anti-patterns are considered wrong to due performance reasons or violate the "rules" of Dioxus. These rules are
+//! borrowed from other successful UI frameworks, and Dioxus is more focused on providing a familiar, ergonomic interface
+//! rather than building new harder-to-misuse patterns.
+use std::collections::HashMap;
+
+use dioxus::prelude::*;
+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
+/// 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.
+///
+/// Dioxus will log an error in the console if it detects that your iterator does not properly generate keys
+#[derive(PartialEq, Props)]
+struct NoKeysProps {
+    data: HashMap<u32, String>,
+}
+static AntipatternNoKeys: FC<NoKeysProps> = |cx| {
+    // WRONG: Make sure to add keys!
+    rsx!(in cx, ul {
+        {cx.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
+    });
+    // Like this:
+    rsx!(in cx, ul {
+        {cx.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
+    })
+};
+
+/// Antipattern: Deeply nested fragments
+/// ------------------------------------
+///
+/// This particular antipattern is not necessarily an antipattern in other frameworks but does has a performance impact
+/// in Dioxus apps. Fragments don't mount a physical element to the dom immediately, so Dioxus must recurse into its
+/// children to find a physical dom node. This process is called "normalization". Other frameworks perform an agressive
+/// mutative normalization while Dioxus keeps your VNodes immutable. This means that deepely nested fragments make Dioxus
+/// perform unnecessary work. Prefer one or two levels of fragments / nested components until presenting a true dom element.
+///
+/// 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 Blah: FC<()> = |cx| {
+    // Try to avoid
+    rsx!(in cx,
+        Fragment {
+            Fragment {
+                Fragment {
+                    Fragment {
+                        Fragment {
+                            div { "Finally have a real node!" }
+                        }
+                    }
+                }
+            }
+        }
+    )
+};

+ 0 - 143
examples/app.rs

@@ -1,143 +0,0 @@
-#![allow(unused)]
-/*
-This example shows how to encapsulate sate in dioxus components with the reducer pattern.
-This pattern is very useful when a single component can handle many types of input that can
-be represented by an enum. This particular pattern is very powerful in rust where ADTs can simplify
-much of the traditional reducer boilerplate.
-*/
-
-fn main() {}
-use dioxus::prelude::*;
-use std::future::Future;
-
-enum Actions {
-    Pause,
-    Play,
-}
-
-struct SomeState {
-    is_playing: bool,
-}
-
-impl SomeState {
-    fn new() -> Self {
-        Self { is_playing: false }
-    }
-    fn reduce(&mut self, action: Actions) {
-        match action {
-            Actions::Pause => self.is_playing = false,
-            Actions::Play => self.is_playing = true,
-        }
-    }
-    fn is_playing(&self) -> &'static str {
-        match self.is_playing {
-            true => "currently playing!",
-            false => "not currently playing",
-        }
-    }
-}
-
-pub static ExampleReducer: FC<()> = |ctx| {
-    let (state, reduce) = use_reducer(&ctx, SomeState::new, SomeState::reduce);
-
-    let is_playing = state.is_playing();
-
-    ctx.render(rsx! {
-        div {
-            h1 {"Select an option"}
-            h3 {"The radio is... {is_playing}!"}
-            button {
-                "Pause"
-                onclick: move |_| reduce(Actions::Pause)
-            }
-            button {
-                "Play"
-                onclick: move |_| reduce(Actions::Play)
-            }
-        }
-    })
-};
-
-/*
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-*/
-
-struct AppContext {
-    name: String,
-}
-
-enum KindaState {
-    Ready,
-    Complete,
-    Erred,
-}
-
-static EnumReducer: FC<()> = |ctx| {
-    let (state, reduce) = use_reducer(&ctx, || KindaState::Ready, |cur, new| *cur = new);
-
-    let status = match state {
-        KindaState::Ready => "Ready",
-        KindaState::Complete => "Complete",
-        KindaState::Erred => "Erred",
-    };
-
-    ctx.render(rsx! {
-        div {
-            h1 {"{status}"}
-            button {
-                "Set Ready"
-                onclick: move |_| reduce(KindaState::Ready)
-            }
-            button {
-                "Set Complete"
-                onclick: move |_| reduce(KindaState::Complete)
-            }
-            button {
-                "Set Erred"
-                onclick: move |_| reduce(KindaState::Erred)
-            }
-            ul {
-                {(0..10).map(|f| {
-
-                    rsx!{
-                        li {
-                            "hello there!"
-                        }
-                    }
-                })}
-            }
-        }
-    })
-};
-
-/// Demonstrate how the DebugRenderer can be used to unit test components without needing a browser
-/// These tests can run locally.
-/// They use the "compare" method of the debug renderer to do partial tree compares for succint
-#[test]
-fn ensure_it_works_properly() -> dioxus::error::Result<()> {
-    let mut test = DebugRenderer::new(EnumReducer);
-    test.compare(rsx! { div { h1 {"Ready"} } })?;
-
-    test.trigger_listener(1)?;
-    test.compare(rsx! { div { h1 {"Ready"} } })?;
-
-    test.trigger_listener(2)?;
-    test.compare(rsx! { div { h1 {"Complete"} } })?;
-
-    test.trigger_listener(3)?;
-    test.compare(rsx! { div { h1 {"Erred"} } })?;
-    Ok(())
-}

+ 56 - 0
examples/reducer.rs

@@ -0,0 +1,56 @@
+//! Example: Reducer Pattern
+//! -----------------
+//! This example shows how to encapsulate sate in dioxus components with the reducer pattern.
+//! This pattern is very useful when a single component can handle many types of input that can
+//! be represented by an enum.
+
+fn main() {}
+use dioxus::prelude::*;
+
+pub static ExampleReducer: FC<()> = |ctx| {
+    let (state, reduce) = use_reducer(&ctx, PlayerState::new, PlayerState::reduce);
+
+    let is_playing = state.is_playing();
+
+    ctx.render(rsx! {
+        div {
+            h1 {"Select an option"}
+            h3 {"The radio is... {is_playing}!"}
+            button {
+                "Pause"
+                onclick: move |_| reduce(PlayerAction::Pause)
+            }
+            button {
+                "Play"
+                onclick: move |_| reduce(PlayerAction::Play)
+            }
+        }
+    })
+};
+
+enum PlayerAction {
+    Pause,
+    Play,
+}
+
+struct PlayerState {
+    is_playing: bool,
+}
+
+impl PlayerState {
+    fn new() -> Self {
+        Self { is_playing: false }
+    }
+    fn reduce(&mut self, action: PlayerAction) {
+        match action {
+            PlayerAction::Pause => self.is_playing = false,
+            PlayerAction::Play => self.is_playing = true,
+        }
+    }
+    fn is_playing(&self) -> &'static str {
+        match self.is_playing {
+            true => "currently playing!",
+            false => "not currently playing",
+        }
+    }
+}

+ 6 - 3
packages/core-macro/examples/prop_test.rs

@@ -2,18 +2,21 @@ fn main() {}
 
 pub mod dioxus {
     pub mod prelude {
-        pub unsafe trait Properties {
+        pub trait Properties {
             type Builder;
-            const CAN_BE_MEMOIZED: bool;
             fn builder() -> Self::Builder;
+            unsafe fn memoize(&self, other: &Self) -> bool;
         }
     }
 }
-#[derive(dioxus_core_macro::Props)]
+
+/// This implementation should require a "PartialEq" because it memoizes (no external references)
+#[derive(PartialEq, dioxus_core_macro::Props)]
 struct SomeProps {
     a: String,
 }
 
+/// This implementation does not require a "PartialEq" because it does not memoize
 #[derive(dioxus_core_macro::Props)]
 struct SomePropsTwo<'a> {
     a: &'a str,

+ 13 - 3
packages/core/src/events.rs

@@ -44,11 +44,21 @@ pub enum VirtualEvent {
     MouseEvent(Rc<dyn on::MouseEvent>),
     PointerEvent(Rc<dyn on::PointerEvent>),
 
+    // image event has conflicting method types
     // ImageEvent(event_data::ImageEvent),
     OtherEvent,
 }
 
 pub mod on {
+    //! This module defines the synthetic events that all Dioxus apps enable. No matter the platform, every dioxus renderer
+    //! will implement the same events and same behavior (bubbling, cancelation, etc).
+    //!
+    //! Synthetic events are immutable and wrapped in Arc. It is the intention for Dioxus renderers to re-use the underyling
+    //! Arc allocation through "get_mut"
+    //!
+    //!
+    //!
+
     #![allow(unused)]
     use std::{fmt::Debug, ops::Deref, rc::Rc};
 
@@ -178,8 +188,8 @@ pub mod on {
 
     pub trait MouseEvent: Debug {
         fn alt_key(&self) -> bool;
-        fn button(&self) -> i32;
-        fn buttons(&self) -> i32;
+        fn button(&self) -> i16;
+        fn buttons(&self) -> u16;
         fn client_x(&self) -> i32;
         fn client_y(&self) -> i32;
         fn ctrl_key(&self) -> bool;
@@ -189,7 +199,7 @@ pub mod on {
         fn screen_x(&self) -> i32;
         fn screen_y(&self) -> i32;
         fn shift_key(&self) -> bool;
-        fn get_modifier_state(&self, key_code: usize) -> bool;
+        fn get_modifier_state(&self, key_code: &str) -> bool;
     }
 
     pub trait PointerEvent: Debug {

+ 3 - 3
packages/core/src/nodebuilder.rs

@@ -504,9 +504,9 @@ where
             let child = item.into_vnode(&self.ctx);
             self.children.push(child);
         }
-        if self.children.len() > len_before + 1 {
-            if self.children.last().unwrap().key().is_none() {
-                if cfg!(debug_assertions) {
+        if cfg!(debug_assertions) {
+            if self.children.len() > len_before + 1 {
+                if self.children.last().unwrap().key().is_none() {
                     log::error!(
                         r#"
 Warning: Each child in an array or iterator should have a unique "key" prop. 

+ 30 - 0
packages/html-namespace/examples/poc.rs

@@ -0,0 +1,30 @@
+//! POC: Planning the layout of a single element type
+//!
+//!
+//! The ultimate goal with a dedicated namespace is three-fold:
+//! - compile-time correct templates preventing misuse of elemnents
+//! - deep integration of DSL with IDE
+//!
+//!
+//!
+
+struct NodeCtx {}
+
+struct div<'a>(&NodeCtx);
+impl<'a> div<'a> {
+    fn new(cx: &NodeCtx) -> Self {
+        div(cx)
+    }
+}
+
+fn main() {}
+
+fn factory(
+    // this is your mom
+    cx: &NodeCtx,
+) {
+    div::new(cx);
+    rsx! {
+        div {}
+    }
+}

+ 19 - 16
packages/web/src/new.rs

@@ -480,43 +480,46 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
             pub struct CustomMouseEvent(web_sys::MouseEvent);
             impl dioxus_core::events::on::MouseEvent for CustomMouseEvent {
                 fn alt_key(&self) -> bool {
-                    todo!()
+                    self.0.alt_key()
                 }
-                fn button(&self) -> i32 {
-                    todo!()
+                fn button(&self) -> i16 {
+                    self.0.button()
                 }
-                fn buttons(&self) -> i32 {
-                    todo!()
+                fn buttons(&self) -> u16 {
+                    self.0.buttons()
                 }
                 fn client_x(&self) -> i32 {
-                    todo!()
+                    self.0.client_x()
                 }
                 fn client_y(&self) -> i32 {
-                    todo!()
+                    self.0.client_y()
                 }
                 fn ctrl_key(&self) -> bool {
-                    todo!()
+                    self.0.ctrl_key()
                 }
                 fn meta_key(&self) -> bool {
-                    todo!()
+                    self.0.meta_key()
                 }
                 fn page_x(&self) -> i32 {
-                    todo!()
+                    self.0.page_x()
                 }
                 fn page_y(&self) -> i32 {
-                    todo!()
+                    self.0.page_y()
                 }
                 fn screen_x(&self) -> i32 {
-                    todo!()
+                    self.0.screen_x()
                 }
                 fn screen_y(&self) -> i32 {
-                    todo!()
+                    self.0.screen_y()
                 }
                 fn shift_key(&self) -> bool {
-                    todo!()
+                    self.0.shift_key()
                 }
-                fn get_modifier_state(&self, key_code: usize) -> bool {
-                    todo!()
+
+                // yikes
+                // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
+                fn get_modifier_state(&self, key_code: &str) -> bool {
+                    self.0.get_modifier_state(key_code)
                 }
             }
             VirtualEvent::MouseEvent(Rc::new(CustomMouseEvent(evt)))

+ 8 - 6
src/lib.rs

@@ -182,12 +182,14 @@ pub mod prelude {
     pub use dioxus_core::prelude::*;
     pub use dioxus_core_macro::fc;
 }
-pub mod builder {
-    // pub use dioxus_core::builder::*;
-}
-pub mod events {
-    // pub use dioxus_core::events::*;
-}
+// pub mod builder {
+//     // pub use dioxus_core::builder::*;
+// }
+pub use dioxus_core::builder;
+pub use dioxus_core::events;
+// pub mod events {
+//     // pub use dioxus_core::events::*;
+// }
 // Just a heads-up, the core functionality of dioxus rests in Dioxus-Core. This crate just wraps a bunch of utilities
 // together and exports their namespaces to something predicatble.
 #[cfg(feature = "core")]