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

This commit is contained in:
Jonathan Kelley committed 2022-01-02 18:35:38 -05:00
1 parent d1560450ba
commit cafb7df736
66 files changed
+792 -2019

No files matched your search

+22 -18
View File
@@ -32,16 +32,20 @@ use dioxus::prelude::*;
struct NoKeysProps {
data: std::collections::HashMap<u32, String>,
}
static AntipatternNoKeys: Component<NoKeysProps> = |cx| {
fn AntipatternNoKeys(cx: Scope<NoKeysProps>) -> Element {
// WRONG: Make sure to add keys!
rsx!(cx, ul {
{cx.props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
cx.render(rsx! {
ul {
cx.props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))
}
});
// RIGHT: Like this:
rsx!(cx, ul {
{cx.props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
cx.render(rsx! {
ul {
cx.props.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))
}
})
};
}
/// Antipattern: Deeply nested fragments
/// ------------------------------------
@@ -54,9 +58,9 @@ static AntipatternNoKeys: Component<NoKeysProps> = |cx| {
///
/// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing
/// an API for registering shared state without the ContextProvider pattern.
static AntipatternNestedFragments: Component = |cx| {
fn AntipatternNestedFragments(cx: Scope<()>) -> Element {
// Try to avoid heavily nesting fragments
rsx!(cx,
cx.render(rsx!(
Fragment {
Fragment {
Fragment {
@@ -68,8 +72,8 @@ static AntipatternNestedFragments: Component = |cx| {
}
}
}
)
};
))
}
/// Antipattern: Using state after it's been updated
/// -----------------------------------------------
@@ -82,13 +86,13 @@ static AntipatternNestedFragments: Component = |cx| {
/// However, calling set_state will *not* update the current version of state in the component. This should be easy to
/// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state`
/// merely places a new value in the queue and schedules the component for a future update.
static AntipatternRelyingOnSetState: Component = |cx| {
fn AntipatternRelyingOnSetState(cx: Scope) -> Element {
let (state, set_state) = use_state(&cx, || "Hello world").classic();
set_state("New state");
// This will return false! `state` will *still* be "Hello world"
assert!(state == &"New state");
todo!()
};
}
/// Antipattern: Capitalization
/// ---------------------------
@@ -120,16 +124,16 @@ static antipattern_component: Component = |cx| todo!();
struct MisuedHooksProps {
should_render_state: bool,
}
static AntipatternMisusedHooks: Component<MisuedHooksProps> = |cx| {
fn AntipatternMisusedHooks(cx: Scope<MisuedHooksProps>) -> Element {
if props.should_render_state {
// do not place a hook in the conditional!
// prefer to move it out of the conditional
let (state, set_state) = use_state(&cx, || "hello world").classic();
rsx!(cx, div { "{state}" })
cx.render(rsx!(div { "{state}" }))
} else {
rsx!(cx, div { "Not rendering state" })
cx.render(rsx!(div { "Not rendering state" }))
}
};
}
/// Antipattern: Downcasting refs and panicking
/// ------------------------------------------
@@ -173,9 +177,9 @@ static _example: Component = |cx| todo!();
/// libraries.
static __example: Component = |cx| todo!();
pub static Example: Component = |cx| {
fn Example(cx: Scope) -> Element {
cx.render(rsx! {
AntipatternNoKeys { data: std::collections::HashMap::new() }
AntipatternNestedFragments {}
})
};
}
+10 -9
View File
@@ -9,7 +9,7 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
Greeting {
@@ -18,20 +18,21 @@ pub static Example: Component = |cx| {
}
}
})
};
#[derive(PartialEq, Props)]
struct GreetingProps {
name: &'static str,
}
static Greeting: Component<GreetingProps> = |cx| {
#[derive(PartialEq, Props)]
struct GreetingProps<'a> {
name: &'static str,
children: Element<'a>,
}
pub fn Greeting<'a>(cx: Scope<'a, GreetingProps<'a>>) -> Element {
cx.render(rsx! {
div {
h1 { "Hello, {cx.props.name}!" }
p { "Welcome to the Dioxus framework" }
br {}
{cx.children()}
&cx.props.children
}
})
};
}
+10 -7
View File
@@ -18,7 +18,7 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| {
fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
Banner {
@@ -29,16 +29,19 @@ pub static Example: Component = |cx| {
}
}
})
};
}
pub static Banner: Component = |cx| {
#[derive(Props)]
struct BannerProps {
children: Element<'a>,
}
fn Banner(cx: Scope) -> Element {
cx.render(rsx! {
div {
h1 { "This is a great banner!" }
div { class: "content"
{cx.children()}
}
div { class: "content", &cx.props.children }
footer { "Wow, what a great footer" }
}
})
};
}
@@ -16,15 +16,15 @@ use dioxus::prelude::*;
pub struct MyProps {
should_show: bool,
}
pub static Example0: Component<MyProps> = |cx| {
pub fn Example0(cx: Scope<MyProps>) -> Element {
cx.render(rsx! {
div {
{cx.props.should_show.then(|| rsx!{
cx.props.should_show.then(|| rsx!{
h1 { "showing the title!" }
})}
})
}
})
};
}
// Convert a boolean conditional into an either/or
// Because rsx! is lazy (produces a closure), we cannot use it in two branch arms. To use it in matching/branching, we
@@ -39,31 +39,29 @@ pub static Example0: Component<MyProps> = |cx| {
pub struct MyProps1 {
should_show: bool,
}
pub static Example1: Component<MyProps1> = |cx| {
pub fn Example1(cx: Scope<MyProps1>) -> Element {
cx.render(rsx! {
div {
// With matching
{match props.should_show {
match props.should_show {
true => cx.render(rsx!(div {"it is true!"})),
false => rsx!(cx, div {"it is false!"}),
}}
},
// or with just regular conditions
{if props.should_show {
if props.should_show {
rsx!(cx, div {"it is true!"})
} else {
rsx!(cx, div {"it is false!"})
}}
},
// or with optional chaining
{
props.should_show
props.should_show
.then(|| rsx!(cx, div {"it is true!"}))
.unwrap_or_else(|| rsx!(cx, div {"it is false!"}))
}
}
})
};
}
/// Matching can be expanded
@@ -77,19 +75,19 @@ pub enum Color {
pub struct MyProps2 {
color: Color,
}
pub static Example2: Component<MyProps2> = |cx| {
pub fn Example2(cx: Scope<MyProps2>) -> Element {
cx.render(rsx! {
div {
{match props.color {
match props.color {
Color::Green => rsx!(cx, div {"it is Green!"}),
Color::Yellow => rsx!(cx, div {"it is Yellow!"}),
Color::Red => rsx!(cx, div {"it is Red!"}),
}}
}
}
})
};
}
pub static Example: Component = |cx| {
pub fn Example(cx: Scope<()>) -> Element {
let should_show = use_state(&cx, || false);
let mut color_index = use_state(&cx, || 0);
let color = match *color_index % 2 {
@@ -101,11 +99,11 @@ pub static Example: Component = |cx| {
cx.render(rsx! {
div {
button {
onclick: move |_| should_show.set(!*should_show)
onclick: move |_| should_show.set(!*should_show),
"click to toggle showing the examples"
}
button {
onclick: move |_| color_index += 1
onclick: move |_| color_index += 1,
"click to for the enxt color"
}
Example0 { should_show: *should_show }
@@ -113,4 +111,4 @@ pub static Example: Component = |cx| {
Example2 { color: color }
}
})
};
}
+6 -6
View File
@@ -1,16 +1,16 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
}
})
};
}
// A controlled component:
static ControlledSelect: Component = |cx| {
pub fn ControlledSelect(cx: Scope) -> Element {
let value = use_state(&cx, || String::from("Grapefruit"));
cx.render(rsx! {
select { value: "{value}", onchange: move |evt| value.set(evt.value()),
@@ -20,10 +20,10 @@ static ControlledSelect: Component = |cx| {
option { value: "Mango", "Mango"}
}
})
};
}
// TODO - how do uncontrolled things work?
static UncontrolledSelect: Component = |cx| {
pub fn UncontrolledSelect(cx: Scope) -> Element {
let value = use_state(&cx, || String::new());
cx.render(rsx! {
@@ -34,4 +34,4 @@ static UncontrolledSelect: Component = |cx| {
option { value: "Mango", "Mango"}
}
})
};
}
+2 -2
View File
@@ -11,7 +11,7 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div {
custom_element {
@@ -19,7 +19,7 @@ pub static Example: Component = |cx| {
}
}
})
};
}
mod dioxus_elements {
use std::fmt::Arguments;
+3 -1
View File
@@ -5,4 +5,6 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| cx.render(rsx! { Fragment {} });
pub fn Example(cx: Scope) -> Element {
None
}
+20 -16
View File
@@ -23,20 +23,20 @@ fn main() {}
/// This is one way to go about error handling (just toss things away with unwrap).
/// However, if you get it wrong, the whole app will crash.
/// This is pretty flimsy.
static App: Component = |cx| {
pub fn App(cx: Scope) -> Element {
let data = get_data().unwrap();
cx.render(rsx!( div { "{data}" } ))
};
}
/// This is a pretty verbose way of error handling
/// However, it's still pretty good since we don't panic, just fail to render anything
static App1: Component = |cx| {
pub fn App1(cx: Scope) -> Element {
let data = match get_data() {
Some(data) => data,
None => return None,
};
cx.render(rsx!( div { "{data}" } ))
};
}
/// This is an even better form of error handling.
/// However, it _does_ make the component go blank, which might not be desirable.
@@ -46,33 +46,37 @@ static App1: Component = |cx| {
/// a user is logged in.
///
/// Dioxus will throw an error in the console if the None-path is ever taken.
static App2: Component = |cx| {
pub fn App2(cx: Scope) -> Element {
let data = get_data()?;
cx.render(rsx!( div { "{data}" } ))
};
}
/// This is top-tier error handling since it displays a failure state.
///
/// However, the error is lacking in context.
static App3: Component = |cx| match get_data() {
Some(data) => cx.render(rsx!( div { "{data}" } )),
None => cx.render(rsx!( div { "Failed to load data :(" } )),
};
pub fn App3(cx: Scope) -> Element {
match get_data() {
Some(data) => cx.render(rsx!( div { "{data}" } )),
None => cx.render(rsx!( div { "Failed to load data :(" } )),
}
}
/// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts
/// a Result<T, V> into an Option<T> and lets you abort rendering by early-returning `None`
static App4: Component = |cx| {
pub fn App4(cx: Scope) -> Element {
let data = get_data_err().ok()?;
cx.render(rsx!( div { "{data}" } ))
};
}
/// This is great error handling since it displays a failure state... with context!
///
/// Hopefully you'll never need to display a screen like this. It's rather bad taste
static App5: Component = |cx| match get_data_err() {
Ok(data) => cx.render(rsx!( div { "{data}" } )),
Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
};
pub fn App5(cx: Scope) -> Element {
match get_data_err() {
Ok(data) => cx.render(rsx!( div { "{data}" } )),
Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
}
}
// this fetching function produces "nothing"
fn get_data() -> Option<String> {
+14 -14
View File
@@ -10,17 +10,25 @@
use dioxus::prelude::*;
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
App1 {}
App2 {}
App3 {}
})
}
// Returning multiple elements with rsx! or html!
static App1: Component = |cx| {
pub fn App1(cx: Scope) -> Element {
cx.render(rsx! {
h1 { }
h2 { }
h3 { }
})
};
}
// Using the Fragment component
static App2: Component = |cx| {
pub fn App2(cx: Scope) -> Element {
cx.render(rsx! {
Fragment {
div {}
@@ -28,10 +36,10 @@ static App2: Component = |cx| {
"asd"
}
})
};
}
// Using the `fragment` method on the NodeFactory
static App3: Component = |cx| {
pub fn App3(cx: Scope) -> Element {
cx.render(LazyNodes::new(move |fac| {
fac.fragment_from_iter([
fac.text(format_args!("A")),
@@ -40,12 +48,4 @@ static App3: Component = |cx| {
fac.text(format_args!("B")),
])
}))
};
pub static Example: Component = |cx| {
cx.render(rsx! {
App1 {}
App2 {}
App3 {}
})
};
}
+2 -2
View File
@@ -19,7 +19,7 @@ h1 {color: blue;}
p {color: red;}
"#;
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
head { style { "{STYLE}" } }
body {
@@ -27,4 +27,4 @@ pub static Example: Component = |cx| {
p {"This is a paragraph"}
}
})
};
}
+4 -4
View File
@@ -10,7 +10,7 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
head {
style: { background_color: "powderblue" }
@@ -24,15 +24,15 @@ pub static Example: Component = |cx| {
}
}
})
};
}
// .... technically the rsx! macro is slightly broken at the moment and allows styles not wrapped in style {}
// I haven't noticed any name collisions yet, and am tentatively leaving this behavior in..
// Don't rely on it.
static Example2: Component = |cx| {
pub fn Example2(cx: Scope) -> Element {
cx.render(rsx! {
div { color: "red"
"hello world!"
}
})
};
}
+11 -16
View File
@@ -15,25 +15,20 @@ use dioxus::prelude::*;
pub static Example: Component = |cx| {
let example_data = use_state(&cx, || 0);
let v = (0..10).map(|f| {
rsx! {
li { onclick: move |_| example_data.set(f)
"ID: {f}"
ul {
(0..10).map(|k| rsx!{
li {
"Sub iterator: {f}.{k}"
}
})
}
}
}
});
cx.render(rsx! {
h3 {"Selected: {example_data}"}
ul {
{v}
(0..10).map(|f| rsx! {
li {
onclick: move |_| example_data.set(f),
"ID: {f}"
ul {
(0..10).map(|k| rsx!{
li { "Sub iterator: {f}.{k}" }
})
}
}
})
}
})
};
+21 -22
View File
@@ -7,50 +7,49 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
ButtonList {}
NonUpdatingEvents {}
DisablePropagation {}
})
};
}
/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
static ButtonList: Component = |cx| {
pub fn ButtonList(cx: Scope) -> Element {
let name = use_state(&cx, || "...?");
let names = ["jack", "jill", "john", "jane"]
.iter()
.map(move |n| rsx!(button { onclick: move |_| name.set(n), "{n}" }));
cx.render(rsx!(
div {
h1 { "Hello, {name}" }
{names}
["jack", "jill", "john", "jane"]
.iter()
.map(move |n| rsx!(button { onclick: move |_| name.set(n), "{n}" }))
}
))
};
}
/// This shows how listeners may be without a visible change in the display.
/// Check the console.
static NonUpdatingEvents: Component = |cx| {
rsx!(cx, div {
button {
onclick: move |_| log::info!("Did not cause any updates!")
"Click me to log!"
pub fn NonUpdatingEvents(cx: Scope) -> Element {
cx.render(rsx! {
div {
button {
onclick: move |_| log::info!("Did not cause any updates!"),
"Click me to log!"
}
}
})
};
}
static DisablePropagation: Component = |cx| {
rsx!(cx,
pub fn DisablePropagation(cx: Scope) -> Element {
cx.render(rsx! {
div {
onclick: move |_| log::info!("event propagated to the div!")
button {
onclick: move |evt| {
log::info!("Button will allow propagation");
}
onclick: move |evt| log::info!("Button will allow propagation"),
}
}
)
};
})
}
+7 -7
View File
@@ -21,11 +21,11 @@ use dioxus::prelude::*;
// By default, components with no props are always memoized.
// A props of () is considered empty.
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
cx.render(rsx! {
div { "100% memoized!" }
})
};
}
// These props do not borrow any content, and therefore can be safely memoized.
// However, the parent *must* create a new string on every render.
@@ -35,11 +35,11 @@ pub struct MyProps1 {
name: String,
}
pub static Example1: Component<MyProps1> = |cx| {
pub fn Example1(cx: Scope<MyProps1>) -> Element {
cx.render(rsx! {
div { "100% memoized! {cx.props.name}" }
})
};
}
// These props do not borrow any content, and therefore can be safely memoized.
// In contrast with the `String` example, these props use `Rc<str>` which operates similar to strings in JavaScript.
@@ -49,11 +49,11 @@ pub struct MyProps2 {
name: std::rc::Rc<str>,
}
pub static Example2: Component<MyProps2> = |cx| {
pub fn Example2(cx: Scope<MyProps2>) -> Element {
cx.render(rsx! {
div { "100% memoized! {cx.props.name}" }
})
};
}
// These props *do* borrow any content, and therefore cannot be safely memoized!.
#[derive(PartialEq, Props)]
@@ -61,7 +61,7 @@ pub struct MyProps3<'a> {
name: &'a str,
}
// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
// Using the `pub static Example: Component` pattern _will_ specify a lifetime, but that lifetime will be static which might
// Using the `pub fn Example(cx: Scope): Component` pattern _will_ specify a lifetime, but that lifetime will be static which might
// not exactly be what you want
fn Example3(cx: Scope<'a, MyProps3<'a>>) -> DomTree {
cx.render(rsx! {
-12
View File
@@ -1,12 +0,0 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: Component = |cx| {
let p = 10;
cx.render(rsx! {
div {
}
})
};
-10
View File
@@ -1,10 +0,0 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: Component = |cx| {
cx.render(rsx! {
div {
}
})
};
+9 -7
View File
@@ -9,7 +9,7 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
let props = MyProps {
count: 0,
live: true,
@@ -18,22 +18,24 @@ pub static Example: Component = |cx| {
cx.render(rsx! {
Example1 { ..props, count: 10, div {"child"} }
})
};
}
#[derive(PartialEq, Props)]
pub struct MyProps {
#[derive(Props)]
pub struct MyProps<'a> {
count: u32,
live: bool,
name: &'static str,
children: Element<'a>,
}
pub static Example1: Component<MyProps> = |cx, MyProps { count, live, name }| {
pub fn Example1(cx: Scope<MyProps>) -> Element {
let MyProps { count, live, name } = cx.props;
cx.render(rsx! {
div {
h1 { "Hello, {name}"}
h3 {"Are we alive? {live}"}
p {"Count is {count}"}
{ cx.children() }
&cx.props.children
}
})
};
}
@@ -1,10 +0,0 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: Component = |cx| {
cx.render(rsx! {
div {
}
})
};
+2 -2
View File
@@ -14,7 +14,7 @@ struct DogApi {
}
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
pub static Example: Component = |cx| {
pub fn Example(cx: Scope) -> Element {
let doggo = use_suspense(
cx,
|| surf::get(ENDPOINT).recv_json::<DogApi>(),
@@ -35,4 +35,4 @@ pub static Example: Component = |cx| {
{doggo}
}
))
};
}
+3 -3
View File
@@ -42,19 +42,19 @@ pub static Example: Component = |cx| {
div {
h1 {"count is {count}"}
button {
onclick: move |_| task.stop(),
"Stop counting"
onclick: move |_| task.stop()
}
button {
onclick: move |_| task.resume(),
"Start counting"
onclick: move |_| task.resume()
}
button {
"Switch counting direcion"
onclick: move |_| {
direction *= -1;
task.restart();
}
"Switch counting direcion"
}
}
})
-9
View File
@@ -1,9 +0,0 @@
use dioxus::prelude::*;
pub static Example: Component = |cx| {
cx.render(rsx! {
div {
}
})
};
-25
View File
@@ -1,25 +0,0 @@
use dioxus::prelude::*;
use dioxus::ssr;
pub static Example: Component = |cx| {
let as_string = use_state(&cx, || {
// Currently, SSR is only supported for whole VirtualDOMs
// This is an easy/low hanging fruit to improve upon
let mut dom = VirtualDom::new(SomeApp);
dom.rebuild();
ssr::render_vdom(&dom)
});
cx.render(rsx! {
div { "{as_string}" }
})
};
static SomeApp: Component = |cx| {
cx.render(rsx! {
div { style: {background_color: "blue"}
h1 {"Some amazing app or component"}
p {"Things are great"}
}
})
};