wip: docs

Worked a bit on adding more examples. Trying out a new "antipattern" example to show how *not* to use Dioxus.
This commit is contained in:
Jonathan Kelley committed 2021-06-23 22:32:54 -04:00
1 parent 9d7ee79826
commit f5683a2346
9 files changed
+197 -174

No files matched your search

+62
View File
@@ -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!" }
}
}
}
}
}
)
};
-143
View File
@@ -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
View File
@@ -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",
}
}
}