Update docs to reflect changes in master (#670)
* work on updating docs to master version * more updates * more polishing * finish interactivity chapter * finish updating core guide * fix grammer mistakes and typos * more grammer fixes * add liveview guide * remove doc build * WIP custom renderer docs * add axum as dev-dependancy to guide * fix examples * fix overview example * use md book fork to fix compilation
This commit is contained in:
1 parent
a616a8fa9d
commit
cd4474cc4f
51 files changed
+870
-549
No files matched your search
@@ -5,13 +5,14 @@ fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: boolean_attribute
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
hidden: "false",
|
||||
"hello"
|
||||
}
|
||||
})
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
hidden: "false",
|
||||
"hello"
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: boolean_attribute
|
||||
}
|
||||
@@ -5,49 +5,50 @@ fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
// ANCHOR: OptionalProps_usage
|
||||
Title {
|
||||
title: "Some Title",
|
||||
},
|
||||
Title {
|
||||
title: "Some Title",
|
||||
subtitle: "Some Subtitle",
|
||||
},
|
||||
// Providing an Option explicitly won't compile though:
|
||||
// Title {
|
||||
// title: "Some Title",
|
||||
// subtitle: None,
|
||||
// },
|
||||
Title {
|
||||
title: "Some Title",
|
||||
},
|
||||
Title {
|
||||
title: "Some Title",
|
||||
subtitle: "Some Subtitle",
|
||||
},
|
||||
// Providing an Option explicitly won't compile though:
|
||||
// Title {
|
||||
// title: "Some Title",
|
||||
// subtitle: None,
|
||||
// },
|
||||
// ANCHOR_END: OptionalProps_usage
|
||||
|
||||
// ANCHOR: ExplicitOption_usage
|
||||
ExplicitOption {
|
||||
title: "Some Title",
|
||||
subtitle: None,
|
||||
},
|
||||
ExplicitOption {
|
||||
title: "Some Title",
|
||||
subtitle: Some("Some Title"),
|
||||
},
|
||||
// This won't compile:
|
||||
// ExplicitOption {
|
||||
// title: "Some Title",
|
||||
// },
|
||||
ExplicitOption {
|
||||
title: "Some Title",
|
||||
subtitle: None,
|
||||
},
|
||||
ExplicitOption {
|
||||
title: "Some Title",
|
||||
subtitle: Some("Some Title"),
|
||||
},
|
||||
// This won't compile:
|
||||
// ExplicitOption {
|
||||
// title: "Some Title",
|
||||
// },
|
||||
// ANCHOR_END: ExplicitOption_usage
|
||||
|
||||
// ANCHOR: DefaultComponent_usage
|
||||
DefaultComponent {
|
||||
number: 5,
|
||||
},
|
||||
DefaultComponent {},
|
||||
DefaultComponent {
|
||||
number: 5,
|
||||
},
|
||||
DefaultComponent {},
|
||||
// ANCHOR_END: DefaultComponent_usage
|
||||
|
||||
// ANCHOR: IntoComponent_usage
|
||||
IntoComponent {
|
||||
string: "some &str",
|
||||
},
|
||||
IntoComponent {
|
||||
string: "some &str",
|
||||
},
|
||||
// ANCHOR_END: IntoComponent_usage
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![allow(unused)]
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus::prelude::*;
|
||||
|
||||
@@ -16,6 +17,7 @@ pub fn App(cx: Scope) -> Element {
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[rustfmt::skip]
|
||||
fn LogIn<'a>(
|
||||
cx: Scope<'a>,
|
||||
is_logged_in: bool,
|
||||
@@ -23,38 +25,73 @@ fn LogIn<'a>(
|
||||
on_log_out: EventHandler<'a>,
|
||||
) -> Element<'a> {
|
||||
// ANCHOR: if_else
|
||||
if *is_logged_in {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"Welcome!",
|
||||
button {
|
||||
onclick: move |_| on_log_out.call(()),
|
||||
"Log Out",
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cx.render(rsx! {
|
||||
button {
|
||||
onclick: move |_| on_log_in.call(()),
|
||||
"Log In",
|
||||
}
|
||||
})
|
||||
}
|
||||
if *is_logged_in {
|
||||
cx.render(rsx! {
|
||||
"Welcome!"
|
||||
button {
|
||||
onclick: move |_| on_log_out.call(()),
|
||||
"Log Out",
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cx.render(rsx! {
|
||||
button {
|
||||
onclick: move |_| on_log_in.call(()),
|
||||
"Log In",
|
||||
}
|
||||
})
|
||||
}
|
||||
// ANCHOR_END: if_else
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[rustfmt::skip]
|
||||
fn LogInImproved<'a>(
|
||||
cx: Scope<'a>,
|
||||
is_logged_in: bool,
|
||||
on_log_in: EventHandler<'a>,
|
||||
on_log_out: EventHandler<'a>,
|
||||
) -> Element<'a> {
|
||||
// ANCHOR: if_else_improved
|
||||
cx.render(rsx! {
|
||||
// We only render the welcome message if we are logged in
|
||||
// You can use if statements in the middle of a render block to conditionally render elements
|
||||
if *is_logged_in {
|
||||
// Notice the body of this if statment is rsx code, not an expression
|
||||
"Welcome!"
|
||||
}
|
||||
button {
|
||||
// depending on the value of `is_logged_in`, we will call a different event handler
|
||||
onclick: move |_| if *is_logged_in {
|
||||
on_log_in.call(())
|
||||
}
|
||||
else{
|
||||
on_log_out.call(())
|
||||
},
|
||||
if *is_logged_in {
|
||||
// if we are logged in, the button should say "Log Out"
|
||||
"Log Out"
|
||||
} else {
|
||||
// if we are not logged in, the button should say "Log In"
|
||||
"Log In"
|
||||
}
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: if_else_improved
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
#[rustfmt::skip]
|
||||
fn LogInWarning(cx: Scope, is_logged_in: bool) -> Element {
|
||||
// ANCHOR: conditional_none
|
||||
if *is_logged_in {
|
||||
return cx.render(rsx!(()));
|
||||
}
|
||||
if *is_logged_in {
|
||||
return None;
|
||||
}
|
||||
|
||||
cx.render(rsx! {
|
||||
a {
|
||||
"You must be logged in to comment"
|
||||
}
|
||||
})
|
||||
cx.render(rsx! {
|
||||
a {
|
||||
"You must be logged in to comment"
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: conditional_none
|
||||
}
|
||||
@@ -5,15 +5,16 @@ fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: dangerous_inner_html
|
||||
// this should come from a trusted source
|
||||
let contents = "live <b>dangerously</b>";
|
||||
// this should come from a trusted source
|
||||
let contents = "live <b>dangerously</b>";
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
dangerous_inner_html: "{contents}",
|
||||
}
|
||||
})
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
dangerous_inner_html: "{contents}",
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: dangerous_inner_html
|
||||
}
|
||||
@@ -5,13 +5,14 @@ fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: rsx
|
||||
cx.render(rsx! {
|
||||
button {
|
||||
onclick: move |event| println!("Clicked! Event: {event:?}"),
|
||||
"click me!"
|
||||
}
|
||||
})
|
||||
cx.render(rsx! {
|
||||
button {
|
||||
onclick: move |event| println!("Clicked! Event: {event:?}"),
|
||||
"click me!"
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: rsx
|
||||
}
|
||||
@@ -5,20 +5,21 @@ fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: rsx
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
onclick: move |_event| {},
|
||||
"outer",
|
||||
button {
|
||||
onclick: move |event| {
|
||||
// now, outer won't be triggered
|
||||
event.stop_propagation();
|
||||
},
|
||||
"inner"
|
||||
}
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
onclick: move |_event| {},
|
||||
"outer",
|
||||
button {
|
||||
onclick: move |event| {
|
||||
// now, outer won't be triggered
|
||||
event.stop_propagation();
|
||||
},
|
||||
"inner"
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: rsx
|
||||
}
|
||||
@@ -5,13 +5,14 @@ fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: prevent_default
|
||||
cx.render(rsx! {
|
||||
input {
|
||||
prevent_default: "oninput",
|
||||
prevent_default: "onclick",
|
||||
}
|
||||
})
|
||||
cx.render(rsx! {
|
||||
input {
|
||||
prevent_default: "oninput",
|
||||
prevent_default: "onclick",
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: prevent_default
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
// ANCHOR: all
|
||||
#![allow(non_snake_case)]
|
||||
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
// launch the dioxus app in a webview
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
// ANCHOR: component
|
||||
// define a component that renders a div with the text "Hello, world!"
|
||||
fn App(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// ANCHOR: all
|
||||
use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
// ANCHOR: glue
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into();
|
||||
|
||||
let view = dioxus_liveview::LiveViewPool::new();
|
||||
|
||||
let app = Router::new()
|
||||
// The root route contains the glue code to connect to the WebSocket
|
||||
.route(
|
||||
"/",
|
||||
get(move || async move {
|
||||
Html(format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>Dioxus LiveView with Axum</title> </head>
|
||||
<body> <div id="main"></div> </body>
|
||||
{glue}
|
||||
</html>
|
||||
"#,
|
||||
// Create the glue code to connect to the WebSocket on the "/ws" route
|
||||
glue = dioxus_liveview::interpreter_glue(&format!("ws://{addr}/ws"))
|
||||
))
|
||||
}),
|
||||
)
|
||||
// The WebSocket route is what Dioxus uses to communicate with the browser
|
||||
.route(
|
||||
"/ws",
|
||||
get(move |ws: WebSocketUpgrade| async move {
|
||||
ws.on_upgrade(move |socket| async move {
|
||||
// When the WebSocket is upgraded, launch the LiveView with the app component
|
||||
_ = view.launch(dioxus_liveview::axum_socket(socket), app).await;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
axum::Server::bind(&addr.to_string().parse().unwrap())
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
// ANCHOR_END: glue
|
||||
|
||||
// ANCHOR: app
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"Hello, world!"
|
||||
}
|
||||
})
|
||||
}
|
||||
// ANCHOR_END: app
|
||||
// ANCHOR_END: all
|
||||
@@ -1,20 +1,17 @@
|
||||
#![allow(non_snake_case)]
|
||||
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
// launch the app in the terminal
|
||||
dioxus_tui::launch(App);
|
||||
}
|
||||
|
||||
// create a component that renders a div with the text "Hello, world!"
|
||||
fn App(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
width: "100%",
|
||||
height: "10px",
|
||||
background_color: "red",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
|
||||
"Hello world!"
|
||||
"Hello, world!"
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
#![allow(non_snake_case)]
|
||||
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
// launch the web app
|
||||
dioxus_web::launch(App);
|
||||
}
|
||||
|
||||
// create a component that renders a div with the text "Hello, world!"
|
||||
fn App(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
@@ -7,60 +7,61 @@ fn main() {
|
||||
dioxus_desktop::launch(App);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
let you_are_happy = true;
|
||||
let you_know_it = false;
|
||||
|
||||
// ANCHOR: conditional
|
||||
// ❌ don't call hooks in conditionals!
|
||||
// We must ensure that the same hooks will be called every time
|
||||
// But `if` statements only run if the conditional is true!
|
||||
// So we might violate rule 2.
|
||||
if you_are_happy && you_know_it {
|
||||
let something = use_state(cx, || "hands");
|
||||
println!("clap your {something}")
|
||||
}
|
||||
|
||||
// ✅ instead, *always* call use_state
|
||||
// You can put other stuff in the conditional though
|
||||
// ❌ don't call hooks in conditionals!
|
||||
// We must ensure that the same hooks will be called every time
|
||||
// But `if` statements only run if the conditional is true!
|
||||
// So we might violate rule 2.
|
||||
if you_are_happy && you_know_it {
|
||||
let something = use_state(cx, || "hands");
|
||||
if you_are_happy && you_know_it {
|
||||
println!("clap your {something}")
|
||||
}
|
||||
println!("clap your {something}")
|
||||
}
|
||||
|
||||
// ✅ instead, *always* call use_state
|
||||
// You can put other stuff in the conditional though
|
||||
let something = use_state(cx, || "hands");
|
||||
if you_are_happy && you_know_it {
|
||||
println!("clap your {something}")
|
||||
}
|
||||
// ANCHOR_END: conditional
|
||||
|
||||
// ANCHOR: closure
|
||||
// ❌ don't call hooks inside closures!
|
||||
// We can't guarantee that the closure, if used, will be called at the same time every time
|
||||
let _a = || {
|
||||
let b = use_state(cx, || 0);
|
||||
b.get()
|
||||
};
|
||||
|
||||
// ✅ instead, move hook `b` outside
|
||||
// ❌ don't call hooks inside closures!
|
||||
// We can't guarantee that the closure, if used, will be called in the same order every time
|
||||
let _a = || {
|
||||
let b = use_state(cx, || 0);
|
||||
let _a = || b.get();
|
||||
b.get()
|
||||
};
|
||||
|
||||
// ✅ instead, move hook `b` outside
|
||||
let b = use_state(cx, || 0);
|
||||
let _a = || b.get();
|
||||
// ANCHOR_END: closure
|
||||
|
||||
let names: Vec<&str> = vec![];
|
||||
|
||||
// ANCHOR: loop
|
||||
// `names` is a Vec<&str>
|
||||
// `names` is a Vec<&str>
|
||||
|
||||
// ❌ Do not use hooks in loops!
|
||||
// In this case, if the length of the Vec changes, we break rule 2
|
||||
for _name in &names {
|
||||
let is_selected = use_state(cx, || false);
|
||||
println!("selected: {is_selected}");
|
||||
}
|
||||
// ❌ Do not use hooks in loops!
|
||||
// In this case, if the length of the Vec changes, we break rule 2
|
||||
for _name in &names {
|
||||
let is_selected = use_state(cx, || false);
|
||||
println!("selected: {is_selected}");
|
||||
}
|
||||
|
||||
// ✅ Instead, use a hashmap with use_ref
|
||||
let selection_map = use_ref(cx, HashMap::<&str, bool>::new);
|
||||
// ✅ Instead, use a hashmap with use_ref
|
||||
let selection_map = use_ref(cx, HashMap::<&str, bool>::new);
|
||||
|
||||
for name in &names {
|
||||
let is_selected = selection_map.read()[name];
|
||||
println!("selected: {is_selected}");
|
||||
}
|
||||
for name in &names {
|
||||
let is_selected = selection_map.read()[name];
|
||||
println!("selected: {is_selected}");
|
||||
}
|
||||
// ANCHOR_END: loop
|
||||
|
||||
cx.render(rsx!(()))
|
||||
|
||||
@@ -7,12 +7,25 @@ fn main() {
|
||||
|
||||
// ANCHOR: component
|
||||
fn App(cx: Scope) -> Element {
|
||||
// count will be initialized to 0 the first time the component is rendered
|
||||
let mut count = use_state(cx, || 0);
|
||||
|
||||
cx.render(rsx!(
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
button {
|
||||
onclick: move |_| {
|
||||
// changing the count will cause the component to re-render
|
||||
count += 1
|
||||
},
|
||||
"Up high!"
|
||||
}
|
||||
button {
|
||||
onclick: move |_| {
|
||||
// changing the count will cause the component to re-render
|
||||
count -= 1
|
||||
},
|
||||
"Down low!"
|
||||
}
|
||||
))
|
||||
}
|
||||
// ANCHOR_END: component
|
||||
@@ -8,13 +8,12 @@ fn main() {
|
||||
// ANCHOR: component
|
||||
fn App(cx: Scope) -> Element {
|
||||
let list = use_ref(cx, Vec::new);
|
||||
let list_formatted = format!("{:?}", *list.read());
|
||||
|
||||
cx.render(rsx!(
|
||||
p { "Current list: {list_formatted}" }
|
||||
p { "Current list: {list.read():?}" }
|
||||
button {
|
||||
onclick: move |event| {
|
||||
list.write().push(event)
|
||||
list.with_mut(|list| list.push(event));
|
||||
},
|
||||
"Click me!"
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ fn main() {
|
||||
struct DarkMode(bool);
|
||||
// ANCHOR_END: DarkMode_struct
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: context_provider
|
||||
use_shared_state_provider(cx, || DarkMode(false));
|
||||
use_shared_state_provider(cx, || DarkMode(false));
|
||||
// ANCHOR_END: context_provider
|
||||
|
||||
let is_dark_mode = use_is_dark_mode(cx);
|
||||
@@ -34,9 +35,10 @@ pub fn App(cx: Scope) -> Element {
|
||||
}))
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn use_is_dark_mode(cx: &ScopeState) -> bool {
|
||||
// ANCHOR: use_context
|
||||
let dark_mode_context = use_shared_state::<DarkMode>(cx);
|
||||
let dark_mode_context = use_shared_state::<DarkMode>(cx);
|
||||
// ANCHOR_END: use_context
|
||||
|
||||
dark_mode_context
|
||||
|
||||
@@ -12,42 +12,80 @@ struct Comment {
|
||||
id: usize,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: render_list
|
||||
let comment_field = use_state(cx, String::new);
|
||||
let mut next_id = use_state(cx, || 0);
|
||||
let comments = use_ref(cx, Vec::<Comment>::new);
|
||||
let comment_field = use_state(cx, String::new);
|
||||
let mut next_id = use_state(cx, || 0);
|
||||
let comments = use_ref(cx, Vec::<Comment>::new);
|
||||
|
||||
let comments_lock = comments.read();
|
||||
let comments_rendered = comments_lock.iter().map(|comment| {
|
||||
cx.render(rsx!(CommentComponent {
|
||||
let comments_lock = comments.read();
|
||||
let comments_rendered = comments_lock.iter().map(|comment| {
|
||||
rsx!(CommentComponent {
|
||||
key: "{comment.id}",
|
||||
comment: comment.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
cx.render(rsx!(
|
||||
form {
|
||||
onsubmit: move |_| {
|
||||
comments.write().push(Comment {
|
||||
content: comment_field.get().clone(),
|
||||
id: *next_id.get(),
|
||||
});
|
||||
next_id += 1;
|
||||
|
||||
comment_field.set(String::new());
|
||||
},
|
||||
input {
|
||||
value: "{comment_field}",
|
||||
oninput: |event| comment_field.set(event.value.clone()),
|
||||
}
|
||||
input {
|
||||
r#type: "submit",
|
||||
}
|
||||
},
|
||||
comments_rendered,
|
||||
))
|
||||
// ANCHOR_END: render_list
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn AppForLoop(cx: Scope) -> Element {
|
||||
// ANCHOR: render_list_for_loop
|
||||
let comment_field = use_state(cx, String::new);
|
||||
let mut next_id = use_state(cx, || 0);
|
||||
let comments = use_ref(cx, Vec::<Comment>::new);
|
||||
|
||||
cx.render(rsx!(
|
||||
form {
|
||||
onsubmit: move |_| {
|
||||
comments.write().push(Comment {
|
||||
content: comment_field.get().clone(),
|
||||
id: *next_id.get(),
|
||||
});
|
||||
next_id += 1;
|
||||
|
||||
comment_field.set(String::new());
|
||||
},
|
||||
input {
|
||||
value: "{comment_field}",
|
||||
oninput: |event| comment_field.set(event.value.clone()),
|
||||
}
|
||||
input {
|
||||
r#type: "submit",
|
||||
}
|
||||
},
|
||||
for comment in &*comments.read() {
|
||||
// Notice the body of this for loop is rsx code, not an expression
|
||||
CommentComponent {
|
||||
key: "{comment.id}",
|
||||
comment: comment.clone(),
|
||||
}))
|
||||
});
|
||||
|
||||
cx.render(rsx!(
|
||||
form {
|
||||
onsubmit: move |_| {
|
||||
comments.write().push(Comment {
|
||||
content: comment_field.get().clone(),
|
||||
id: *next_id.get(),
|
||||
});
|
||||
next_id += 1;
|
||||
|
||||
comment_field.set(String::new());
|
||||
},
|
||||
input {
|
||||
value: "{comment_field}",
|
||||
oninput: |event| comment_field.set(event.value.clone()),
|
||||
}
|
||||
input {
|
||||
r#type: "submit",
|
||||
}
|
||||
},
|
||||
comments_rendered,
|
||||
))
|
||||
// ANCHOR_END: render_list
|
||||
}
|
||||
}
|
||||
))
|
||||
// ANCHOR_END: render_list_for_loop
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
|
||||
@@ -19,91 +19,151 @@ pub fn App(cx: Scope) -> Element {
|
||||
))
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn Empty(cx: Scope) -> Element {
|
||||
// ANCHOR: empty
|
||||
cx.render(rsx!(div {}))
|
||||
cx.render(rsx!(div {
|
||||
// attributes / listeners
|
||||
// children
|
||||
}))
|
||||
// ANCHOR_END: empty
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn Children(cx: Scope) -> Element {
|
||||
// ANCHOR: children
|
||||
cx.render(rsx!(ol {
|
||||
li {"First Item"}
|
||||
li {"Second Item"}
|
||||
li {"Third Item"}
|
||||
}))
|
||||
cx.render(rsx!(ol {
|
||||
li {"First Item"}
|
||||
li {"Second Item"}
|
||||
li {"Third Item"}
|
||||
}))
|
||||
// ANCHOR_END: children
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn Fragments(cx: Scope) -> Element {
|
||||
// ANCHOR: fragments
|
||||
cx.render(rsx!(
|
||||
p {"First Item"},
|
||||
p {"Second Item"},
|
||||
Fragment {
|
||||
span { "a group" },
|
||||
span { "of three" },
|
||||
span { "items" },
|
||||
}
|
||||
))
|
||||
cx.render(rsx!(
|
||||
p {"First Item"},
|
||||
p {"Second Item"},
|
||||
Fragment {
|
||||
span { "a group" },
|
||||
span { "of three" },
|
||||
span { "items" },
|
||||
}
|
||||
))
|
||||
// ANCHOR_END: fragments
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn ManyRoots(cx: Scope) -> Element {
|
||||
// ANCHOR: manyroots
|
||||
cx.render(rsx!(
|
||||
p {"First Item"},
|
||||
p {"Second Item"},
|
||||
))
|
||||
// ANCHOR_END: manyroots
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn Attributes(cx: Scope) -> Element {
|
||||
// ANCHOR: attributes
|
||||
cx.render(rsx!(a {
|
||||
href: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
class: "primary_button",
|
||||
"Log In"
|
||||
}))
|
||||
cx.render(rsx!(a {
|
||||
href: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
class: "primary_button",
|
||||
color: "red",
|
||||
}))
|
||||
// ANCHOR_END: attributes
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn VariableAttributes(cx: Scope) -> Element {
|
||||
// ANCHOR: variable_attributes
|
||||
let written_in_rust = true;
|
||||
let button_type = "button";
|
||||
cx.render(rsx!(button {
|
||||
disabled: "{written_in_rust}",
|
||||
class: "{button_type}",
|
||||
"Rewrite it in rust"
|
||||
}))
|
||||
let written_in_rust = true;
|
||||
let button_type = "button";
|
||||
cx.render(rsx!(button {
|
||||
disabled: "{written_in_rust}",
|
||||
class: "{button_type}",
|
||||
"Rewrite it in rust"
|
||||
}))
|
||||
// ANCHOR_END: variable_attributes
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn CustomAttributes(cx: Scope) -> Element {
|
||||
// ANCHOR: custom_attributes
|
||||
cx.render(rsx!(b {
|
||||
"customAttribute": "value",
|
||||
"Rust is Cool"
|
||||
}))
|
||||
// ANCHOR_END: custom_attributes
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn Formatting(cx: Scope) -> Element {
|
||||
// ANCHOR: formatting
|
||||
let coordinates = (42, 0);
|
||||
let country = "es";
|
||||
cx.render(rsx!(div {
|
||||
class: "country-{country}",
|
||||
"Coordinates: {coordinates:?}",
|
||||
// arbitrary expressions are allowed,
|
||||
// as long as they don't contain `{}`
|
||||
div {
|
||||
"{country.to_uppercase()}"
|
||||
},
|
||||
div {
|
||||
"{7*6}"
|
||||
},
|
||||
}))
|
||||
// ANCHOR_END: formatting
|
||||
let coordinates = (42, 0);
|
||||
let country = "es";
|
||||
cx.render(rsx!(div {
|
||||
class: "country-{country}",
|
||||
"position": "{coordinates:?}",
|
||||
// arbitrary expressions are allowed,
|
||||
// as long as they don't contain `{}`
|
||||
div {
|
||||
"{country.to_uppercase()}"
|
||||
},
|
||||
div {
|
||||
"{7*6}"
|
||||
},
|
||||
// {} can be escaped with {{}}
|
||||
div {
|
||||
"{{}}"
|
||||
},
|
||||
}))
|
||||
// ANCHOR_END: formatting
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn Expression(cx: Scope) -> Element {
|
||||
// ANCHOR: expression
|
||||
let text = "Dioxus";
|
||||
cx.render(rsx!(span {
|
||||
text.to_uppercase()
|
||||
}))
|
||||
let text = "Dioxus";
|
||||
cx.render(rsx!(span {
|
||||
text.to_uppercase(),
|
||||
// create a list of text from 0 to 9
|
||||
(0..10).map(|i| rsx!{ i.to_string() })
|
||||
}))
|
||||
// ANCHOR_END: expression
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn Loops(cx: Scope) -> Element {
|
||||
// ANCHOR: loops
|
||||
cx.render(rsx!{
|
||||
// use a for loop where the body itself is RSX
|
||||
div {
|
||||
// create a list of text from 0 to 9
|
||||
for i in 0..3 {
|
||||
// NOTE: the body of the loop is RSX not a rust statement
|
||||
div {
|
||||
"{i}"
|
||||
}
|
||||
}
|
||||
}
|
||||
// iterator equivalent
|
||||
div {
|
||||
(0..3).map(|i| rsx!{ div { "{i}" } })
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: loops
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn IfStatements(cx: Scope) -> Element {
|
||||
// ANCHOR: ifstatements
|
||||
cx.render(rsx!{
|
||||
// use if statements without an else
|
||||
if true {
|
||||
rsx!(div { "true" })
|
||||
}
|
||||
})
|
||||
// ANCHOR_END: ifstatements
|
||||
}
|
||||
@@ -12,49 +12,51 @@ struct ApiResponse {
|
||||
image_url: String,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn App(cx: Scope) -> Element {
|
||||
// ANCHOR: use_future
|
||||
let future = use_future(cx, (), |_| async move {
|
||||
reqwest::get("https://dog.ceo/api/breeds/image/random")
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<ApiResponse>()
|
||||
.await
|
||||
});
|
||||
// ANCHOR_END: use_future
|
||||
// ANCHOR: use_future
|
||||
let future = use_future(cx, (), |_| async move {
|
||||
reqwest::get("https://dog.ceo/api/breeds/image/random")
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<ApiResponse>()
|
||||
.await
|
||||
});
|
||||
// ANCHOR_END: use_future
|
||||
|
||||
// ANCHOR: render
|
||||
cx.render(match future.value() {
|
||||
Some(Ok(response)) => rsx! {
|
||||
button {
|
||||
onclick: move |_| future.restart(),
|
||||
"Click to fetch another doggo"
|
||||
// ANCHOR: render
|
||||
cx.render(match future.value() {
|
||||
Some(Ok(response)) => rsx! {
|
||||
button {
|
||||
onclick: move |_| future.restart(),
|
||||
"Click to fetch another doggo"
|
||||
}
|
||||
div {
|
||||
img {
|
||||
max_width: "500px",
|
||||
max_height: "500px",
|
||||
src: "{response.image_url}",
|
||||
}
|
||||
div {
|
||||
img {
|
||||
max_width: "500px",
|
||||
max_height: "500px",
|
||||
src: "{response.image_url}",
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(Err(_)) => rsx! { div { "Loading dogs failed" } },
|
||||
None => rsx! { div { "Loading dogs..." } },
|
||||
})
|
||||
// ANCHOR_END: render
|
||||
}
|
||||
},
|
||||
Some(Err(_)) => rsx! { div { "Loading dogs failed" } },
|
||||
None => rsx! { div { "Loading dogs..." } },
|
||||
})
|
||||
// ANCHOR_END: render
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[inline_props]
|
||||
fn RandomDog(cx: Scope, breed: String) -> Element {
|
||||
// ANCHOR: dependency
|
||||
let future = use_future(cx, (breed,), |(breed,)| async move {
|
||||
reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<ApiResponse>()
|
||||
.await
|
||||
});
|
||||
// ANCHOR_END: dependency
|
||||
// ANCHOR: dependency
|
||||
let future = use_future(cx, (breed,), |(breed,)| async move {
|
||||
reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<ApiResponse>()
|
||||
.await
|
||||
});
|
||||
// ANCHOR_END: dependency
|
||||
|
||||
cx.render(rsx!(()))
|
||||
}
|
||||
Reference in new issue
Block a user