Merge branch 'upstream' into optimize-templates-v3

This commit is contained in:
Evan Almloff committed 2022-12-07 18:29:02 -06:00
commit 99a4a9d45b
113 files changed
+336 -817

No files matched your search

+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: docs environment: docs
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
# NOTE: Comment out when https://github.com/rust-lang/mdBook/pull/1306 is merged and released # NOTE: Comment out when https://github.com/rust-lang/mdBook/pull/1306 is merged and released
# - name: Setup mdBook # - name: Setup mdBook
@@ -32,7 +32,7 @@ jobs:
cd router && mdbook build -d ../nightly/router && cd .. cd router && mdbook build -d ../nightly/router && cd ..
- name: Deploy 🚀 - name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.2.3 uses: JamesIves/github-pages-deploy-action@v4.4.1
with: with:
branch: gh-pages # The branch the action should deploy to. branch: gh-pages # The branch the action should deploy to.
folder: docs/nightly # The folder the action should deploy. folder: docs/nightly # The folder the action should deploy.
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
name: Test Suite name: Test Suite
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
+5 -5
View File
@@ -32,7 +32,7 @@ jobs:
name: Check name: Check
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
@@ -51,7 +51,7 @@ jobs:
name: Test Suite name: Test Suite
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
@@ -73,7 +73,7 @@ jobs:
name: Rustfmt name: Rustfmt
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
@@ -91,7 +91,7 @@ jobs:
name: Clippy name: Clippy
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
@@ -115,7 +115,7 @@ jobs:
# options: --security-opt seccomp=unconfined # options: --security-opt seccomp=unconfined
# steps: # steps:
# - name: Checkout repository # - name: Checkout repository
# uses: actions/checkout@v2 # uses: actions/checkout@v3
# - name: Generate code coverage # - name: Generate code coverage
# run: | # run: |
# apt-get update &&\ # apt-get update &&\
+1 -1
View File
@@ -48,7 +48,7 @@ jobs:
- name: disable git eol translation - name: disable git eol translation
run: git config --global core.autocrlf false run: git config --global core.autocrlf false
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
# Run build # Run build
- name: Install Rustup using win.rustup.rs - name: Install Rustup using win.rustup.rs
+1 -6
View File
@@ -16,11 +16,6 @@ members = [
"packages/autofmt", "packages/autofmt",
"packages/rsx", "packages/rsx",
"docs/guide", "docs/guide",
# "packages/tui",
# "packages/native-core",
# "packages/native-core-macro",
]
exclude = [
"packages/tui", "packages/tui",
"packages/native-core", "packages/native-core",
"packages/native-core-macro", "packages/native-core-macro",
@@ -40,7 +35,7 @@ homepage = "https://dioxuslabs.com"
documentation = "https://dioxuslabs.com" documentation = "https://dioxuslabs.com"
keywords = ["dom", "ui", "gui", "react", "wasm"] keywords = ["dom", "ui", "gui", "react", "wasm"]
rust-version = "1.60.0" rust-version = "1.60.0"
publish = false
[dev-dependencies] [dev-dependencies]
dioxus = { path = "./packages/dioxus" } dioxus = { path = "./packages/dioxus" }
+1 -1
View File
@@ -54,7 +54,7 @@ Dioxus is a portable, performant, and ergonomic framework for building cross-pla
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
h1 { "High-Five counter: {count}" } h1 { "High-Five counter: {count}" }
@@ -26,7 +26,7 @@ struct ClickableProps<'a> {
// ANCHOR: Clickable // ANCHOR: Clickable
fn Clickable<'a>(cx: Scope<'a, ClickableProps<'a>>) -> Element { fn Clickable<'a>(cx: Scope<'a, ClickableProps<'a>>) -> Element {
match cx.props.children { match cx.props.children {
Some(VNode::Text(_)) => { Ok(VNode { dynamic_nodes, .. }) => {
todo!("render some stuff") todo!("render some stuff")
} }
_ => { _ => {
+2 -2
View File
@@ -6,7 +6,7 @@ fn main() {
} }
pub fn App(cx: Scope) -> Element { pub fn App(cx: Scope) -> Element {
let is_logged_in = use_state(&cx, || false); let is_logged_in = use_state(cx, || false);
cx.render(rsx!(LogIn { cx.render(rsx!(LogIn {
is_logged_in: **is_logged_in, is_logged_in: **is_logged_in,
@@ -48,7 +48,7 @@ fn LogIn<'a>(
fn LogInWarning(cx: Scope, is_logged_in: bool) -> Element { fn LogInWarning(cx: Scope, is_logged_in: bool) -> Element {
// ANCHOR: conditional_none // ANCHOR: conditional_none
if *is_logged_in { if *is_logged_in {
return None; return cx.render(rsx!(()));
} }
cx.render(rsx! { cx.render(rsx! {
+1 -2
View File
@@ -1,6 +1,5 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use dioxus::events::MouseData;
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() { fn main() {
@@ -20,7 +19,7 @@ fn App(cx: Scope) -> Element {
// ANCHOR: component_with_handler // ANCHOR: component_with_handler
#[derive(Props)] #[derive(Props)]
pub struct FancyButtonProps<'a> { pub struct FancyButtonProps<'a> {
on_click: EventHandler<'a, MouseData>, on_click: EventHandler<'a, MouseEvent>,
} }
pub fn FancyButton<'a>(cx: Scope<'a, FancyButtonProps<'a>>) -> Element<'a> { pub fn FancyButton<'a>(cx: Scope<'a, FancyButtonProps<'a>>) -> Element<'a> {
+1 -1
View File
@@ -14,7 +14,7 @@ fn App(cx: Scope) -> Element {
button { button {
onclick: move |event| { onclick: move |event| {
// now, outer won't be triggered // now, outer won't be triggered
event.cancel_bubble(); event.stop_propogation();
}, },
"inner" "inner"
} }
@@ -26,7 +26,7 @@ fn App(cx: Scope) -> Element {
background_color: "red", background_color: "red",
justify_content: "center", justify_content: "center",
align_items: "center", align_items: "center",
onkeydown: move |k: KeyboardEvent| if let KeyCode::Q = k.data.key_code { onkeydown: move |k: KeyboardEvent| if let KeyCode::Q = k.key_code {
tui_ctx.quit(); tui_ctx.quit();
}, },
+7 -7
View File
@@ -17,13 +17,13 @@ fn App(cx: Scope) -> Element {
// But `if` statements only run if the conditional is true! // But `if` statements only run if the conditional is true!
// So we might violate rule 2. // So we might violate rule 2.
if you_are_happy && you_know_it { if you_are_happy && you_know_it {
let something = use_state(&cx, || "hands"); let something = use_state(cx, || "hands");
println!("clap your {something}") println!("clap your {something}")
} }
// ✅ instead, *always* call use_state // ✅ instead, *always* call use_state
// You can put other stuff in the conditional though // You can put other stuff in the conditional though
let something = use_state(&cx, || "hands"); let something = use_state(cx, || "hands");
if you_are_happy && you_know_it { if you_are_happy && you_know_it {
println!("clap your {something}") println!("clap your {something}")
} }
@@ -33,12 +33,12 @@ fn App(cx: Scope) -> Element {
// ❌ don't call hooks inside closures! // ❌ don't call hooks inside closures!
// We can't guarantee that the closure, if used, will be called at the same time every time // We can't guarantee that the closure, if used, will be called at the same time every time
let _a = || { let _a = || {
let b = use_state(&cx, || 0); let b = use_state(cx, || 0);
b.get() b.get()
}; };
// ✅ instead, move hook `b` outside // ✅ instead, move hook `b` outside
let b = use_state(&cx, || 0); let b = use_state(cx, || 0);
let _a = || b.get(); let _a = || b.get();
// ANCHOR_END: closure // ANCHOR_END: closure
@@ -50,12 +50,12 @@ fn App(cx: Scope) -> Element {
// ❌ Do not use hooks in loops! // ❌ Do not use hooks in loops!
// In this case, if the length of the Vec changes, we break rule 2 // In this case, if the length of the Vec changes, we break rule 2
for _name in &names { for _name in &names {
let is_selected = use_state(&cx, || false); let is_selected = use_state(cx, || false);
println!("selected: {is_selected}"); println!("selected: {is_selected}");
} }
// ✅ Instead, use a hashmap with use_ref // ✅ Instead, use a hashmap with use_ref
let selection_map = use_ref(&cx, HashMap::<&str, bool>::new); let selection_map = use_ref(cx, HashMap::<&str, bool>::new);
for name in &names { for name in &names {
let is_selected = selection_map.read()[name]; let is_selected = selection_map.read()[name];
@@ -63,5 +63,5 @@ fn App(cx: Scope) -> Element {
} }
// ANCHOR_END: loop // ANCHOR_END: loop
None cx.render(rsx!(()))
} }
+1 -1
View File
@@ -8,6 +8,6 @@ struct AppSettings {}
// ANCHOR: wrap_context // ANCHOR: wrap_context
fn use_settings(cx: &ScopeState) -> UseSharedState<AppSettings> { fn use_settings(cx: &ScopeState) -> UseSharedState<AppSettings> {
use_context::<AppSettings>(cx).expect("App settings not provided") use_shared_state::<AppSettings>(cx).expect("App settings not provided")
} }
// ANCHOR_END: wrap_context // ANCHOR_END: wrap_context
+1 -1
View File
@@ -7,7 +7,7 @@ fn main() {
// ANCHOR: component // ANCHOR: component
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx!( cx.render(rsx!(
h1 { "High-Five counter: {count}" } h1 { "High-Five counter: {count}" }
@@ -8,8 +8,8 @@ fn main() {
// ANCHOR: component // ANCHOR: component
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
// ANCHOR: use_state_calls // ANCHOR: use_state_calls
let mut count_a = use_state(&cx, || 0); let mut count_a = use_state(cx, || 0);
let mut count_b = use_state(&cx, || 0); let mut count_b = use_state(cx, || 0);
// ANCHOR_END: use_state_calls // ANCHOR_END: use_state_calls
cx.render(rsx!( cx.render(rsx!(
+1 -1
View File
@@ -7,7 +7,7 @@ fn main() {
// ANCHOR: component // ANCHOR: component
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
let list = use_ref(&cx, Vec::new); let list = use_ref(cx, Vec::new);
let list_formatted = format!("{:?}", *list.read()); let list_formatted = format!("{:?}", *list.read());
cx.render(rsx!( cx.render(rsx!(
+1 -1
View File
@@ -7,7 +7,7 @@ fn main() {
// ANCHOR: component // ANCHOR: component
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
let name = use_state(&cx, || "bob".to_string()); let name = use_state(cx, || "bob".to_string());
cx.render(rsx! { cx.render(rsx! {
input { input {
+2 -3
View File
@@ -1,7 +1,6 @@
// ANCHOR: all // ANCHOR: all
#![allow(non_snake_case)] #![allow(non_snake_case)]
use dioxus::events::FormData;
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() { fn main() {
@@ -18,7 +17,7 @@ fn MemeEditor(cx: Scope) -> Element {
width: fit-content; width: fit-content;
"; ";
let caption = use_state(&cx, || "me waiting for my rust code to compile".to_string()); let caption = use_state(cx, || "me waiting for my rust code to compile".to_string());
cx.render(rsx! { cx.render(rsx! {
div { div {
@@ -83,7 +82,7 @@ fn Meme<'a>(cx: Scope<'a>, caption: &'a str) -> Element<'a> {
fn CaptionEditor<'a>( fn CaptionEditor<'a>(
cx: Scope<'a>, cx: Scope<'a>,
caption: &'a str, caption: &'a str,
on_input: EventHandler<'a, FormData>, on_input: EventHandler<'a, FormEvent>,
) -> Element<'a> { ) -> Element<'a> {
let input_style = r" let input_style = r"
border: none; border: none;
+8 -9
View File
@@ -1,7 +1,6 @@
// ANCHOR: all // ANCHOR: all
#![allow(non_snake_case)] #![allow(non_snake_case)]
use dioxus::events::FormData;
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() { fn main() {
@@ -14,10 +13,10 @@ struct DarkMode(bool);
pub fn App(cx: Scope) -> Element { pub fn App(cx: Scope) -> Element {
// ANCHOR: context_provider // ANCHOR: context_provider
use_context_provider(&cx, || DarkMode(false)); use_shared_state_provider(cx, || DarkMode(false));
// ANCHOR_END: context_provider // ANCHOR_END: context_provider
let is_dark_mode = use_is_dark_mode(&cx); let is_dark_mode = use_is_dark_mode(cx);
let wrapper_style = if is_dark_mode { let wrapper_style = if is_dark_mode {
r" r"
@@ -37,7 +36,7 @@ pub fn App(cx: Scope) -> Element {
pub fn use_is_dark_mode(cx: &ScopeState) -> bool { pub fn use_is_dark_mode(cx: &ScopeState) -> bool {
// ANCHOR: use_context // ANCHOR: use_context
let dark_mode_context = use_context::<DarkMode>(cx); let dark_mode_context = use_shared_state::<DarkMode>(cx);
// ANCHOR_END: use_context // ANCHOR_END: use_context
dark_mode_context dark_mode_context
@@ -47,7 +46,7 @@ pub fn use_is_dark_mode(cx: &ScopeState) -> bool {
// ANCHOR: toggle // ANCHOR: toggle
pub fn DarkModeToggle(cx: Scope) -> Element { pub fn DarkModeToggle(cx: Scope) -> Element {
let dark_mode = use_context::<DarkMode>(&cx)?; let dark_mode = use_shared_state::<DarkMode>(cx).unwrap();
let style = if dark_mode.read().0 { let style = if dark_mode.read().0 {
"color:white" "color:white"
@@ -71,7 +70,7 @@ pub fn DarkModeToggle(cx: Scope) -> Element {
// ANCHOR: meme_editor // ANCHOR: meme_editor
fn MemeEditor(cx: Scope) -> Element { fn MemeEditor(cx: Scope) -> Element {
let is_dark_mode = use_is_dark_mode(&cx); let is_dark_mode = use_is_dark_mode(cx);
let heading_style = if is_dark_mode { "color: white" } else { "" }; let heading_style = if is_dark_mode { "color: white" } else { "" };
let container_style = r" let container_style = r"
@@ -82,7 +81,7 @@ fn MemeEditor(cx: Scope) -> Element {
width: fit-content; width: fit-content;
"; ";
let caption = use_state(&cx, || "me waiting for my rust code to compile".to_string()); let caption = use_state(cx, || "me waiting for my rust code to compile".to_string());
cx.render(rsx! { cx.render(rsx! {
div { div {
@@ -150,9 +149,9 @@ fn Meme<'a>(cx: Scope<'a>, caption: &'a str) -> Element<'a> {
fn CaptionEditor<'a>( fn CaptionEditor<'a>(
cx: Scope<'a>, cx: Scope<'a>,
caption: &'a str, caption: &'a str,
on_input: EventHandler<'a, FormData>, on_input: EventHandler<'a, FormEvent>,
) -> Element<'a> { ) -> Element<'a> {
let is_dark_mode = use_is_dark_mode(&cx); let is_dark_mode = use_is_dark_mode(cx);
let colors = if is_dark_mode { let colors = if is_dark_mode {
r" r"
+3 -3
View File
@@ -14,9 +14,9 @@ struct Comment {
pub fn App(cx: Scope) -> Element { pub fn App(cx: Scope) -> Element {
// ANCHOR: render_list // ANCHOR: render_list
let comment_field = use_state(&cx, String::new); let comment_field = use_state(cx, String::new);
let mut next_id = use_state(&cx, || 0); let mut next_id = use_state(cx, || 0);
let comments = use_ref(&cx, Vec::<Comment>::new); let comments = use_ref(cx, Vec::<Comment>::new);
let comments_lock = comments.read(); let comments_lock = comments.read();
let comments_rendered = comments_lock.iter().map(|comment| { let comments_rendered = comments_lock.iter().map(|comment| {
+1 -1
View File
@@ -103,7 +103,7 @@ pub fn Expression(cx: Scope) -> Element {
// ANCHOR: expression // ANCHOR: expression
let text = "Dioxus"; let text = "Dioxus";
cx.render(rsx!(span { cx.render(rsx!(span {
[text.to_uppercase()] text.to_uppercase()
})) }))
// ANCHOR_END: expression // ANCHOR_END: expression
} }
+8 -8
View File
@@ -8,7 +8,7 @@ fn main() {
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
// ANCHOR: spawn // ANCHOR: spawn
let logged_in = use_state(&cx, || false); let logged_in = use_state(cx, || false);
let log_in = move |_| { let log_in = move |_| {
cx.spawn({ cx.spawn({
@@ -58,18 +58,18 @@ pub fn Tokio(cx: Scope) -> Element {
// ANCHOR_END: tokio // ANCHOR_END: tokio
}; };
None cx.render(rsx!(()))
} }
pub fn ToOwnedMacro(cx: Scope) -> Element { pub fn ToOwnedMacro(cx: Scope) -> Element {
let count = use_state(&cx, || 0); let count = use_state(cx, || 0);
let age = use_state(&cx, || 0); let age = use_state(cx, || 0);
let name = use_state(&cx, || 0); let name = use_state(cx, || 0);
let description = use_state(&cx, || 0); let description = use_state(cx, || 0);
let _ = || { let _ = || {
// ANCHOR: to_owned_macro // ANCHOR: to_owned_macro
use dioxus::core::to_owned; use dioxus::hooks::to_owned;
cx.spawn({ cx.spawn({
to_owned![count, age, name, description]; to_owned![count, age, name, description];
@@ -80,5 +80,5 @@ pub fn ToOwnedMacro(cx: Scope) -> Element {
// ANCHOR_END: to_owned_macro // ANCHOR_END: to_owned_macro
}; };
None cx.render(rsx!(()))
} }
+3 -3
View File
@@ -14,7 +14,7 @@ struct ApiResponse {
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
// ANCHOR: use_future // ANCHOR: use_future
let future = use_future(&cx, (), |_| async move { let future = use_future(cx, (), |_| async move {
reqwest::get("https://dog.ceo/api/breeds/image/random") reqwest::get("https://dog.ceo/api/breeds/image/random")
.await .await
.unwrap() .unwrap()
@@ -47,7 +47,7 @@ fn App(cx: Scope) -> Element {
#[inline_props] #[inline_props]
fn RandomDog(cx: Scope, breed: String) -> Element { fn RandomDog(cx: Scope, breed: String) -> Element {
// ANCHOR: dependency // ANCHOR: dependency
let future = use_future(&cx, (breed,), |(breed,)| async move { let future = use_future(cx, (breed,), |(breed,)| async move {
reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
.await .await
.unwrap() .unwrap()
@@ -56,5 +56,5 @@ fn RandomDog(cx: Scope, breed: String) -> Element {
}); });
// ANCHOR_END: dependency // ANCHOR_END: dependency
None cx.render(rsx!(()))
} }
@@ -12,7 +12,7 @@ Your component today might look something like this:
```rust ```rust
fn Comp(cx: Scope) -> DomTree { fn Comp(cx: Scope) -> DomTree {
let (title, set_title) = use_state(&cx, || "Title".to_string()); let (title, set_title) = use_state(cx, || "Title".to_string());
cx.render(rsx!{ cx.render(rsx!{
input { input {
value: title, value: title,
@@ -26,7 +26,7 @@ This component is fairly straightforward – the input updates its own value on
```rust ```rust
fn Comp(cx: Scope) -> DomTree { fn Comp(cx: Scope) -> DomTree {
let (title, set_title) = use_state(&cx, || "Title".to_string()); let (title, set_title) = use_state(cx, || "Title".to_string());
cx.render(rsx!{ cx.render(rsx!{
div { div {
input { input {
@@ -49,7 +49,7 @@ We can use signals to generate a two-way binding between data and the input box.
```rust ```rust
fn Comp(cx: Scope) -> DomTree { fn Comp(cx: Scope) -> DomTree {
let mut title = use_signal(&cx, || String::from("Title")); let mut title = use_signal(cx, || String::from("Title"));
cx.render(rsx!(input { value: title })) cx.render(rsx!(input { value: title }))
} }
``` ```
@@ -58,8 +58,8 @@ For a slightly more interesting example, this component calculates the sum betwe
```rust ```rust
fn Calculator(cx: Scope) -> DomTree { fn Calculator(cx: Scope) -> DomTree {
let mut a = use_signal(&cx, || 0); let mut a = use_signal(cx, || 0);
let mut b = use_signal(&cx, || 0); let mut b = use_signal(cx, || 0);
let mut c = a + b; let mut c = a + b;
rsx! { rsx! {
input { value: a } input { value: a }
@@ -72,8 +72,8 @@ fn Calculator(cx: Scope) -> DomTree {
Do you notice how we can use built-in operations on signals? Under the hood, we actually create a new derived signal that depends on `a` and `b`. Whenever `a` or `b` update, then `c` will update. If we need to create a new derived signal that's more complex than a basic operation (`std::ops`) we can either chain signals together or combine them: Do you notice how we can use built-in operations on signals? Under the hood, we actually create a new derived signal that depends on `a` and `b`. Whenever `a` or `b` update, then `c` will update. If we need to create a new derived signal that's more complex than a basic operation (`std::ops`) we can either chain signals together or combine them:
```rust ```rust
let mut a = use_signal(&cx, || 0); let mut a = use_signal(cx, || 0);
let mut b = use_signal(&cx, || 0); let mut b = use_signal(cx, || 0);
// Chain signals together using the `with` method // Chain signals together using the `with` method
let c = a.with(b).map(|(a, b)| *a + *b); let c = a.with(b).map(|(a, b)| *a + *b);
@@ -84,7 +84,7 @@ let c = a.with(b).map(|(a, b)| *a + *b);
If we ever need to get the value out of a signal, we can simply `deref` it. If we ever need to get the value out of a signal, we can simply `deref` it.
```rust ```rust
let mut a = use_signal(&cx, || 0); let mut a = use_signal(cx, || 0);
let c = *a + *b; let c = *a + *b;
``` ```
@@ -97,7 +97,7 @@ Sometimes you want a signal to propagate across your app, either through far-awa
```rust ```rust
const TITLE: Atom<String> = || "".to_string(); const TITLE: Atom<String> = || "".to_string();
const Provider: Component = |cx|{ const Provider: Component = |cx|{
let title = use_signal(&cx, &TITLE); let title = use_signal(cx, &TITLE);
render!(input { value: title }) render!(input { value: title })
}; };
``` ```
@@ -106,7 +106,7 @@ If we use the `TITLE` atom in another component, we can cause updates to flow be
```rust ```rust
const Receiver: Component = |cx|{ const Receiver: Component = |cx|{
let title = use_signal(&cx, &TITLE); let title = use_signal(cx, &TITLE);
log::info!("This will only be called once!"); log::info!("This will only be called once!");
rsx!(cx, rsx!(cx,
div { div {
@@ -133,7 +133,7 @@ Dioxus automatically understands how to use your signals when mixed with iterato
```rust ```rust
const DICT: AtomFamily<String, String> = |_| {}; const DICT: AtomFamily<String, String> = |_| {};
const List: Component = |cx|{ const List: Component = |cx|{
let dict = use_signal(&cx, &DICT); let dict = use_signal(cx, &DICT);
cx.render(rsx!( cx.render(rsx!(
ul { ul {
For { each: dict, map: |k, v| rsx!( li { "{v}" }) } For { each: dict, map: |k, v| rsx!( li { "{v}" }) }
+1 -1
View File
@@ -59,7 +59,7 @@ If we represented the reactive graph presented above in Dioxus, it would look ve
// Declare a component that holds our datasources and calculates `g` // Declare a component that holds our datasources and calculates `g`
fn RenderGraph(cx: Scope) -> Element { fn RenderGraph(cx: Scope) -> Element {
let seconds = use_datasource(SECONDS); let seconds = use_datasource(SECONDS);
let constant = use_state(&cx, || 1); let constant = use_state(cx, || 1);
cx.render(rsx!( cx.render(rsx!(
RenderG { seconds: seconds } RenderG { seconds: seconds }
+6 -6
View File
@@ -28,8 +28,8 @@ If we used global state like use_context or fermi, we might be tempted to inject
```rust ```rust
fn Titlebar(cx: Scope<TitlebarProps>) -> Element { fn Titlebar(cx: Scope<TitlebarProps>) -> Element {
let title = use_read(&cx, TITLE); let title = use_read(cx, TITLE);
let subtitle = use_read(&cx, SUBTITLE); let subtitle = use_read(cx, SUBTITLE);
cx.render(rsx!{/* ui */}) cx.render(rsx!{/* ui */})
} }
@@ -43,11 +43,11 @@ To enable our titlebar component to be used across apps, we want to lift our ato
```rust ```rust
fn DocsiteTitlesection(cx: Scope) { fn DocsiteTitlesection(cx: Scope) {
let title = use_read(&cx, TITLE); let title = use_read(cx, TITLE);
let subtitle = use_read(&cx, SUBTITLE); let subtitle = use_read(cx, SUBTITLE);
let username = use_read(&cx, USERNAME); let username = use_read(cx, USERNAME);
let points = use_read(&cx, POINTS); let points = use_read(cx, POINTS);
cx.render(rsx!{ cx.render(rsx!{
TitleBar { title: title, subtitle: subtitle } TitleBar { title: title, subtitle: subtitle }
+7 -7
View File
@@ -13,7 +13,7 @@ struct Todo {
is_editing: bool, is_editing: bool,
} }
let todos = use_ref(&cx, || vec![Todo::new()]); let todos = use_ref(cx, || vec![Todo::new()]);
cx.render(rsx!{ cx.render(rsx!{
ul { ul {
@@ -40,7 +40,7 @@ Instead, let's refactor our Todo component to handle its own state:
```rust ```rust
#[inline_props] #[inline_props]
fn Todo<'a>(cx: Scope, todo: &'a Todo) -> Element { fn Todo<'a>(cx: Scope, todo: &'a Todo) -> Element {
let is_hovered = use_state(&cx, || false); let is_hovered = use_state(cx, || false);
cx.render(rsx!{ cx.render(rsx!{
li { li {
@@ -80,15 +80,15 @@ struct State {
} }
// in the component // in the component
let state = use_ref(&cx, State::new) let state = use_ref(cx, State::new)
``` ```
The "better" approach for this particular component would be to break the state apart into different values: The "better" approach for this particular component would be to break the state apart into different values:
```rust ```rust
let count = use_state(&cx, || 0); let count = use_state(cx, || 0);
let color = use_state(&cx, || "red"); let color = use_state(cx, || "red");
let names = use_state(&cx, HashMap::new); let names = use_state(cx, HashMap::new);
``` ```
You might recognize that our "names" value is a HashMap – which is not terribly cheap to clone every time we update its value. To solve this issue, we *highly* suggest using a library like [`im`](https://crates.io/crates/im) which will take advantage of structural sharing to make clones and mutations much cheaper. You might recognize that our "names" value is a HashMap – which is not terribly cheap to clone every time we update its value. To solve this issue, we *highly* suggest using a library like [`im`](https://crates.io/crates/im) which will take advantage of structural sharing to make clones and mutations much cheaper.
@@ -96,7 +96,7 @@ You might recognize that our "names" value is a HashMap – which is not terribl
When combined with the `make_mut` method on `use_state`, you can get really succinct updates to collections: When combined with the `make_mut` method on `use_state`, you can get really succinct updates to collections:
```rust ```rust
let todos = use_state(&cx, im_rc::HashMap::default); let todos = use_state(cx, im_rc::HashMap::default);
todos.make_mut().insert("new todo", Todo { todos.make_mut().insert("new todo", Todo {
contents: "go get groceries", contents: "go get groceries",
+2 -2
View File
@@ -8,8 +8,8 @@ For example, let's say we have a component that has two children:
```rust ```rust
fn Demo(cx: Scope) -> Element { fn Demo(cx: Scope) -> Element {
// don't worry about these 2, we'll cover them later // don't worry about these 2, we'll cover them later
let name = use_state(&cx, || String::from("bob")); let name = use_state(cx, || String::from("bob"));
let age = use_state(&cx, || 21); let age = use_state(cx, || 21);
cx.render(rsx!{ cx.render(rsx!{
Name { name: name } Name { name: name }
+1 -1
View File
@@ -20,7 +20,7 @@ Our component is really simple – we just call `use_ref` to get an initial calc
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let state = use_ref(&cx, Calculator::new); let state = use_ref(cx, Calculator::new);
cx.render(rsx!{ cx.render(rsx!{
// the rendering code // the rendering code
+12 -12
View File
@@ -10,7 +10,7 @@ The basic setup for coroutines is the `use_coroutine` hook. Most coroutines we w
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let ws: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move { let ws: &UseCoroutine<()> = use_coroutine(cx, |rx| async move {
// Connect to some sort of service // Connect to some sort of service
let mut conn = connect_to_ws_server().await; let mut conn = connect_to_ws_server().await;
@@ -27,7 +27,7 @@ For many services, a simple async loop will handle the majority of use cases.
However, if we want to temporarily disable the coroutine, we can "pause" it using the `pause` method, and "resume" it using the `resume` method: However, if we want to temporarily disable the coroutine, we can "pause" it using the `pause` method, and "resume" it using the `resume` method:
```rust ```rust
let sync: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move { let sync: &UseCoroutine<()> = use_coroutine(cx, |rx| async move {
// code for syncing // code for syncing
}); });
@@ -63,7 +63,7 @@ enum ProfileUpdate {
SetAge(i32) SetAge(i32)
} }
let profile = use_coroutine(&cx, |mut rx: UnboundedReciver<ProfileUpdate>| async move { let profile = use_coroutine(cx, |mut rx: UnboundedReciver<ProfileUpdate>| async move {
let mut server = connect_to_server().await; let mut server = connect_to_server().await;
while let Ok(msg) = rx.next().await { while let Ok(msg) = rx.next().await {
@@ -86,9 +86,9 @@ cx.render(rsx!{
For sufficiently complex apps, we could build a bunch of different useful "services" that loop on channels to update the app. For sufficiently complex apps, we could build a bunch of different useful "services" that loop on channels to update the app.
```rust ```rust
let profile = use_coroutine(&cx, profile_service); let profile = use_coroutine(cx, profile_service);
let editor = use_coroutine(&cx, editor_service); let editor = use_coroutine(cx, editor_service);
let sync = use_coroutine(&cx, sync_service); let sync = use_coroutine(cx, sync_service);
async fn profile_service(rx: UnboundedReceiver<ProfileCommand>) { async fn profile_service(rx: UnboundedReceiver<ProfileCommand>) {
// do stuff // do stuff
@@ -109,9 +109,9 @@ We can combine coroutines with Fermi to emulate Redux Toolkit's Thunk system wit
static USERNAME: Atom<String> = |_| "default".to_string(); static USERNAME: Atom<String> = |_| "default".to_string();
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let atoms = use_atom_root(&cx); let atoms = use_atom_root(cx);
use_coroutine(&cx, |rx| sync_service(rx, atoms.clone())); use_coroutine(cx, |rx| sync_service(rx, atoms.clone()));
cx.render(rsx!{ cx.render(rsx!{
Banner {} Banner {}
@@ -119,7 +119,7 @@ fn app(cx: Scope) -> Element {
} }
fn Banner(cx: Scope) -> Element { fn Banner(cx: Scope) -> Element {
let username = use_read(&cx, USERNAME); let username = use_read(cx, USERNAME);
cx.render(rsx!{ cx.render(rsx!{
h1 { "Welcome back, {username}" } h1 { "Welcome back, {username}" }
@@ -158,8 +158,8 @@ To yield values from a coroutine, simply bring in a `UseState` handle and set th
```rust ```rust
let sync_status = use_state(&cx, || Status::Launching); let sync_status = use_state(cx, || Status::Launching);
let sync_task = use_coroutine(&cx, |rx: UnboundedReceiver<SyncAction>| { let sync_task = use_coroutine(cx, |rx: UnboundedReceiver<SyncAction>| {
to_owned![sync_status]; to_owned![sync_status];
async move { async move {
loop { loop {
@@ -176,7 +176,7 @@ Coroutine handles are automatically injected through the context API. `use_corou
```rust ```rust
fn Child(cx: Scope) -> Element { fn Child(cx: Scope) -> Element {
let sync_task = use_coroutine_handle::<SyncAction>(&cx); let sync_task = use_coroutine_handle::<SyncAction>(cx);
sync_task.send(SyncAction::SetUsername); sync_task.send(SyncAction::SetUsername);
} }
@@ -56,7 +56,7 @@ The next "best" way of handling errors in Dioxus is to match on the error locall
To do this, we simply have an error state built into our component: To do this, we simply have an error state built into our component:
```rust ```rust
let err = use_state(&cx, || None); let err = use_state(cx, || None);
``` ```
Whenever we perform an action that generates an error, we'll set that error state. We can then match on the error in a number of ways (early return, return Element, etc). Whenever we perform an action that generates an error, we'll set that error state. We can then match on the error in a number of ways (early return, return Element, etc).
@@ -64,7 +64,7 @@ Whenever we perform an action that generates an error, we'll set that error stat
```rust ```rust
fn Commandline(cx: Scope) -> Element { fn Commandline(cx: Scope) -> Element {
let error = use_state(&cx, || None); let error = use_state(cx, || None);
cx.render(match *error { cx.render(match *error {
Some(error) => rsx!( Some(error) => rsx!(
@@ -85,7 +85,7 @@ If you're dealing with a handful of components with minimal nesting, you can jus
```rust ```rust
fn Commandline(cx: Scope) -> Element { fn Commandline(cx: Scope) -> Element {
let error = use_state(&cx, || None); let error = use_state(cx, || None);
if let Some(error) = **error { if let Some(error) = **error {
return cx.render(rsx!{ "An error occured" }); return cx.render(rsx!{ "An error occured" });
@@ -125,7 +125,7 @@ Then, in our top level component, we want to explicitly handle the possible erro
```rust ```rust
fn TopLevel(cx: Scope) -> Element { fn TopLevel(cx: Scope) -> Element {
let error = use_read(&cx, INPUT_ERROR); let error = use_read(cx, INPUT_ERROR);
match error { match error {
TooLong => return cx.render(rsx!{ "FAILED: Too long!" }), TooLong => return cx.render(rsx!{ "FAILED: Too long!" }),
@@ -139,7 +139,7 @@ Now, whenever a downstream component has an error in its actions, it can simply
```rust ```rust
fn Commandline(cx: Scope) -> Element { fn Commandline(cx: Scope) -> Element {
let set_error = use_set(&cx, INPUT_ERROR); let set_error = use_set(cx, INPUT_ERROR);
cx.render(rsx!{ cx.render(rsx!{
input { input {
+1 -1
View File
@@ -6,7 +6,7 @@ Dioxus is a portable, performant, and ergonomic framework for building cross-pla
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx!( cx.render(rsx!(
h1 { "High-Five counter: {count}" } h1 { "High-Five counter: {count}" }
+12 -12
View File
@@ -10,7 +10,7 @@ A configuração básica para corrotinas é o _hook_ `use_coroutine`. A maioria
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let ws: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move { let ws: &UseCoroutine<()> = use_coroutine(cx, |rx| async move {
// Connect to some sort of service // Connect to some sort of service
let mut conn = connect_to_ws_server().await; let mut conn = connect_to_ws_server().await;
@@ -27,7 +27,7 @@ Para muitos serviços, um _loop_ assíncrono simples lidará com a maioria dos c
No entanto, se quisermos desabilitar temporariamente a corrotina, podemos "pausá-la" usando o método `pause` e "retomá-la" usando o método `resume`: No entanto, se quisermos desabilitar temporariamente a corrotina, podemos "pausá-la" usando o método `pause` e "retomá-la" usando o método `resume`:
```rust ```rust
let sync: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move { let sync: &UseCoroutine<()> = use_coroutine(cx, |rx| async move {
// code for syncing // code for syncing
}); });
@@ -62,7 +62,7 @@ enum ProfileUpdate {
SetAge(i32) SetAge(i32)
} }
let profile = use_coroutine(&cx, |mut rx: UnboundedReciver<ProfileUpdate>| async move { let profile = use_coroutine(cx, |mut rx: UnboundedReciver<ProfileUpdate>| async move {
let mut server = connect_to_server().await; let mut server = connect_to_server().await;
while let Ok(msg) = rx.next().await { while let Ok(msg) = rx.next().await {
@@ -85,9 +85,9 @@ cx.render(rsx!{
Para aplicativos suficientemente complexos, poderíamos criar vários "serviços" úteis diferentes que fazem um _loop_ nos canais para atualizar o aplicativo. Para aplicativos suficientemente complexos, poderíamos criar vários "serviços" úteis diferentes que fazem um _loop_ nos canais para atualizar o aplicativo.
```rust ```rust
let profile = use_coroutine(&cx, profile_service); let profile = use_coroutine(cx, profile_service);
let editor = use_coroutine(&cx, editor_service); let editor = use_coroutine(cx, editor_service);
let sync = use_coroutine(&cx, sync_service); let sync = use_coroutine(cx, sync_service);
async fn profile_service(rx: UnboundedReceiver<ProfileCommand>) { async fn profile_service(rx: UnboundedReceiver<ProfileCommand>) {
// do stuff // do stuff
@@ -108,9 +108,9 @@ Podemos combinar corrotinas com `Fermi` para emular o sistema `Thunk` do **Redux
static USERNAME: Atom<String> = |_| "default".to_string(); static USERNAME: Atom<String> = |_| "default".to_string();
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let atoms = use_atom_root(&cx); let atoms = use_atom_root(cx);
use_coroutine(&cx, |rx| sync_service(rx, atoms.clone())); use_coroutine(cx, |rx| sync_service(rx, atoms.clone()));
cx.render(rsx!{ cx.render(rsx!{
Banner {} Banner {}
@@ -118,7 +118,7 @@ fn app(cx: Scope) -> Element {
} }
fn Banner(cx: Scope) -> Element { fn Banner(cx: Scope) -> Element {
let username = use_read(&cx, USERNAME); let username = use_read(cx, USERNAME);
cx.render(rsx!{ cx.render(rsx!{
h1 { "Welcome back, {username}" } h1 { "Welcome back, {username}" }
@@ -156,8 +156,8 @@ async fn sync_service(mut rx: UnboundedReceiver<SyncAction>, atoms: AtomRoot) {
Para obter valores de uma corrotina, basta usar um identificador `UseState` e definir o valor sempre que sua corrotina concluir seu trabalho. Para obter valores de uma corrotina, basta usar um identificador `UseState` e definir o valor sempre que sua corrotina concluir seu trabalho.
```rust ```rust
let sync_status = use_state(&cx, || Status::Launching); let sync_status = use_state(cx, || Status::Launching);
let sync_task = use_coroutine(&cx, |rx: UnboundedReceiver<SyncAction>| { let sync_task = use_coroutine(cx, |rx: UnboundedReceiver<SyncAction>| {
to_owned![sync_status]; to_owned![sync_status];
async move { async move {
loop { loop {
@@ -174,7 +174,7 @@ Os identificadores de corrotina são injetados automaticamente por meio da API d
```rust ```rust
fn Child(cx: Scope) -> Element { fn Child(cx: Scope) -> Element {
let sync_task = use_coroutine_handle::<SyncAction>(&cx); let sync_task = use_coroutine_handle::<SyncAction>(cx);
sync_task.send(SyncAction::SetUsername); sync_task.send(SyncAction::SetUsername);
} }
@@ -53,14 +53,14 @@ A próxima "melhor" maneira de lidar com erros no Dioxus é combinar (`match`) o
Para fazer isso, simplesmente temos um estado de erro embutido em nosso componente: Para fazer isso, simplesmente temos um estado de erro embutido em nosso componente:
```rust ```rust
let err = use_state(&cx, || None); let err = use_state(cx, || None);
``` ```
Sempre que realizarmos uma ação que gere um erro, definiremos esse estado de erro. Podemos então combinar o erro de várias maneiras (retorno antecipado, elemento de retorno etc.). Sempre que realizarmos uma ação que gere um erro, definiremos esse estado de erro. Podemos então combinar o erro de várias maneiras (retorno antecipado, elemento de retorno etc.).
```rust ```rust
fn Commandline(cx: Scope) -> Element { fn Commandline(cx: Scope) -> Element {
let error = use_state(&cx, || None); let error = use_state(cx, || None);
cx.render(match *error { cx.render(match *error {
Some(error) => rsx!( Some(error) => rsx!(
@@ -81,7 +81,7 @@ Se você estiver lidando com alguns componentes com um mínimo de aninhamento, b
```rust ```rust
fn Commandline(cx: Scope) -> Element { fn Commandline(cx: Scope) -> Element {
let error = use_state(&cx, || None); let error = use_state(cx, || None);
if let Some(error) = **error { if let Some(error) = **error {
return cx.render(rsx!{ "An error occured" }); return cx.render(rsx!{ "An error occured" });
@@ -120,7 +120,7 @@ Então, em nosso componente de nível superior, queremos tratar explicitamente o
```rust ```rust
fn TopLevel(cx: Scope) -> Element { fn TopLevel(cx: Scope) -> Element {
let error = use_read(&cx, INPUT_ERROR); let error = use_read(cx, INPUT_ERROR);
match error { match error {
TooLong => return cx.render(rsx!{ "FAILED: Too long!" }), TooLong => return cx.render(rsx!{ "FAILED: Too long!" }),
@@ -134,7 +134,7 @@ Agora, sempre que um componente _downstream_ tiver um erro em suas ações, ele
```rust ```rust
fn Commandline(cx: Scope) -> Element { fn Commandline(cx: Scope) -> Element {
let set_error = use_set(&cx, INPUT_ERROR); let set_error = use_set(cx, INPUT_ERROR);
cx.render(rsx!{ cx.render(rsx!{
input { input {
+1 -1
View File
@@ -6,7 +6,7 @@ Dioxus é uma estrutura portátil, de alto desempenho e ergonômica para a const
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx!( cx.render(rsx!(
h1 { "High-Five counter: {count}" } h1 { "High-Five counter: {count}" }
+7 -7
View File
@@ -27,7 +27,7 @@ Dioxus is a recently-released library for building interactive user interfaces (
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
h1 { "Count: {count}" } h1 { "Count: {count}" }
@@ -102,8 +102,8 @@ We're also using hooks to parse the URL parameters and segments so you can inter
struct Query { name: String } struct Query { name: String }
fn BlogPost(cx: Scope) -> Element { fn BlogPost(cx: Scope) -> Element {
let post = use_route(&cx).segment("post")?; let post = use_route(cx).segment("post")?;
let query = use_route(&cx).query::<Query>()?; let query = use_route(cx).query::<Query>()?;
cx.render(rsx!{ cx.render(rsx!{
"Viewing post {post}" "Viewing post {post}"
@@ -128,7 +128,7 @@ static TITLE: Atom<&str> = |_| "Hello";
// Read the value from anywhere in the app, subscribing to any changes // Read the value from anywhere in the app, subscribing to any changes
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let title = use_read(&cx, TITLE); let title = use_read(cx, TITLE);
cx.render(rsx!{ cx.render(rsx!{
h1 { "{title}" } h1 { "{title}" }
Child {} Child {}
@@ -137,7 +137,7 @@ fn app(cx: Scope) -> Element {
// Set the value from anywhere in the app // Set the value from anywhere in the app
fn Child(cx: Scope) -> Element { fn Child(cx: Scope) -> Element {
let set_title = use_set(&cx, TITLE); let set_title = use_set(cx, TITLE);
cx.render(rsx!{ cx.render(rsx!{
button { button {
onclick: move |_| set_title("goodbye"), onclick: move |_| set_title("goodbye"),
@@ -245,7 +245,7 @@ First, we upgraded the `use_future` hook. It now supports dependencies, which le
```rust ```rust
fn RenderDog(cx: Scope, breed: String) -> Element { fn RenderDog(cx: Scope, breed: String) -> Element {
let dog_request = use_future(&cx, (breed,), |(breed,)| async move { let dog_request = use_future(cx, (breed,), |(breed,)| async move {
reqwest::get(format!("https://dog.ceo/api/breed/{}/images/random", breed)) reqwest::get(format!("https://dog.ceo/api/breed/{}/images/random", breed))
.await .await
.unwrap() .unwrap()
@@ -265,7 +265,7 @@ Additionally, we added better support for coroutines. You can now start, stop, r
```rust ```rust
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
let sync_task = use_coroutine(&cx, |rx| async move { let sync_task = use_coroutine(cx, |rx| async move {
connect_to_server().await; connect_to_server().await;
let state = MyState::new(); let state = MyState::new();
+5 -5
View File
@@ -28,7 +28,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
h1 { "Count: {count}" } h1 { "Count: {count}" }
@@ -138,7 +138,7 @@ struct CardProps {
} }
static Card: Component<CardProps> = |cx| { static Card: Component<CardProps> = |cx| {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx!( cx.render(rsx!(
aside { aside {
h2 { "{cx.props.title}" } h2 { "{cx.props.title}" }
@@ -191,7 +191,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
h1 { "Count: {count}" } h1 { "Count: {count}" }
@@ -260,7 +260,7 @@ Dioxus understands the lifetimes of data borrowed from `Scope`, so you can safel
```rust ```rust
let name = use_state(&cx, || "asd"); let name = use_state(cx, || "asd");
rsx! { rsx! {
div { div {
button { onclick: move |_| name.set("abc") } button { onclick: move |_| name.set("abc") }
@@ -274,7 +274,7 @@ Because we know the lifetime of your handlers, we can also expose this to childr
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let name = use_state(&cx, || "asd"); let name = use_state(cx, || "asd");
cx.render(rsx!{ cx.render(rsx!{
Button { name: name } Button { name: name }
}) })
+2 -2
View File
@@ -153,7 +153,7 @@ use dioxus::{
... ...
fn blog_post(cx: Scope) -> Element { fn blog_post(cx: Scope) -> Element {
let route = use_route(&cx); // NEW let route = use_route(cx); // NEW
let blog_text = ""; let blog_text = "";
cx.render(rsx! { cx.render(rsx! {
@@ -165,7 +165,7 @@ Dioxus Router provides built in methods to extract information from a route. We
The ``segment`` method also parses the parameter into any type for us. We'll use a match expression that handles a parsing error and on success, uses our helper function to grab the blog post. The ``segment`` method also parses the parameter into any type for us. We'll use a match expression that handles a parsing error and on success, uses our helper function to grab the blog post.
```rs ```rs
fn blog_post(cx: Scope) -> Element { fn blog_post(cx: Scope) -> Element {
let route = use_route(&cx); let route = use_route(cx);
// NEW // NEW
let blog_text = match route.segment::<String>("post").unwrap() { let blog_text = match route.segment::<String>("post").unwrap() {
+1 -1
View File
@@ -41,7 +41,7 @@ const RECT_STYLE: &str = r#"
"#; "#;
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let events = use_ref(&cx, std::collections::VecDeque::new); let events = use_ref(cx, std::collections::VecDeque::new);
let log_event = move |event: Event| { let log_event = move |event: Event| {
let mut events = events.write(); let mut events = events.write();
+1 -1
View File
@@ -19,7 +19,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let val = use_state(&cx, || String::from("0")); let val = use_state(cx, || String::from("0"));
let input_digit = move |num: u8| { let input_digit = move |num: u8| {
if val.get() == "0" { if val.get() == "0" {
+4 -4
View File
@@ -16,10 +16,10 @@ pub struct Client {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let clients = use_ref(&cx, || vec![] as Vec<Client>); let clients = use_ref(cx, || vec![] as Vec<Client>);
let firstname = use_state(&cx, String::new); let firstname = use_state(cx, String::new);
let lastname = use_state(&cx, String::new); let lastname = use_state(cx, String::new);
let description = use_state(&cx, String::new); let description = use_state(cx, String::new);
cx.render(rsx!( cx.render(rsx!(
body { body {
+1 -1
View File
@@ -16,7 +16,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
// let nf = NodeFactory::new(&cx); // let nf = NodeFactory::new(cx);
// let mut attrs = dioxus::core::exports::bumpalo::collections::Vec::new_in(nf.bump()); // let mut attrs = dioxus::core::exports::bumpalo::collections::Vec::new_in(nf.bump());
+1 -1
View File
@@ -5,7 +5,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let disabled = use_state(&cx, || false); let disabled = use_state(cx, || false);
cx.render(rsx! { cx.render(rsx! {
div { div {
+1 -1
View File
@@ -5,7 +5,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let val = use_state(&cx, || "0.0001"); let val = use_state(cx, || "0.0001");
let num = match val.parse::<f32>() { let num = match val.parse::<f32>() {
Err(_) => return cx.render(rsx!("Parsing failed")), Err(_) => return cx.render(rsx!("Parsing failed")),
+2 -2
View File
@@ -5,8 +5,8 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let script = use_state(&cx, String::new); let script = use_state(cx, String::new);
let eval = dioxus_desktop::use_eval(&cx); let eval = dioxus_desktop::use_eval(cx);
cx.render(rsx! { cx.render(rsx! {
div { div {
+3 -3
View File
@@ -10,7 +10,7 @@ fn main() {
static NAME: Atom<String> = |_| "world".to_string(); static NAME: Atom<String> = |_| "world".to_string();
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let name = use_read(&cx, NAME); let name = use_read(cx, NAME);
cx.render(rsx! { cx.render(rsx! {
div { "hello {name}!" } div { "hello {name}!" }
@@ -20,7 +20,7 @@ fn app(cx: Scope) -> Element {
} }
fn Child(cx: Scope) -> Element { fn Child(cx: Scope) -> Element {
let set_name = use_set(&cx, NAME); let set_name = use_set(cx, NAME);
cx.render(rsx! { cx.render(rsx! {
button { button {
@@ -33,7 +33,7 @@ fn Child(cx: Scope) -> Element {
static NAMES: AtomRef<Vec<String>> = |_| vec!["world".to_string()]; static NAMES: AtomRef<Vec<String>> = |_| vec!["world".to_string()];
fn ChildWithRef(cx: Scope) -> Element { fn ChildWithRef(cx: Scope) -> Element {
let names = use_atom_ref(&cx, NAMES); let names = use_atom_ref(cx, NAMES);
cx.render(rsx! { cx.render(rsx! {
div { div {
+1 -1
View File
@@ -19,7 +19,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let files = use_ref(&cx, Files::new); let files = use_ref(cx, Files::new);
cx.render(rsx! { cx.render(rsx! {
div { div {
+2 -2
View File
@@ -32,8 +32,8 @@ impl Label {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let items = use_ref(&cx, Vec::new); let items = use_ref(cx, Vec::new);
let selected = use_state(&cx, || None); let selected = use_state(cx, || None);
cx.render(rsx! { cx.render(rsx! {
div { class: "container", div { class: "container",
+1 -1
View File
@@ -20,7 +20,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let val = use_state(&cx, || 0); let val = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
div { div {
+1 -1
View File
@@ -28,7 +28,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let state = use_state(&cx, || 1); let state = use_state(cx, || 1);
cx.render(rsx! { cx.render(rsx! {
div { div {
+1 -1
View File
@@ -36,7 +36,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let state = use_ref(&cx, Calculator::new); let state = use_ref(cx, Calculator::new);
cx.render(rsx! { cx.render(rsx! {
style { include_str!("./assets/calculator.css") } style { include_str!("./assets/calculator.css") }
+1 -1
View File
@@ -15,7 +15,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let state = use_state(&cx, PlayerState::new); let state = use_state(cx, PlayerState::new);
cx.render(rsx!( cx.render(rsx!(
div { div {
+1 -1
View File
@@ -9,7 +9,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
h1 { "High-Five counter: {count}" } h1 { "High-Five counter: {count}" }
+3 -3
View File
@@ -30,7 +30,7 @@ fn app(cx: Scope) -> Element {
} }
fn BlogPost(cx: Scope) -> Element { fn BlogPost(cx: Scope) -> Element {
let post = dioxus_router::use_route(&cx).last_segment().unwrap(); let post = dioxus_router::use_route(cx).last_segment().unwrap();
cx.render(rsx! { cx.render(rsx! {
div { div {
@@ -46,9 +46,9 @@ struct Query {
} }
fn User(cx: Scope) -> Element { fn User(cx: Scope) -> Element {
let post = dioxus_router::use_route(&cx).last_segment().unwrap(); let post = dioxus_router::use_route(cx).last_segment().unwrap();
let query = dioxus_router::use_route(&cx) let query = dioxus_router::use_route(cx)
.query::<Query>() .query::<Query>()
.unwrap_or(Query { bold: false }); .unwrap_or(Query { bold: false });
+3 -3
View File
@@ -13,14 +13,14 @@ fn main() {
} }
fn example(cx: Scope) -> Element { fn example(cx: Scope) -> Element {
let items = use_state(&cx, || { let items = use_state(cx, || {
vec![Thing { vec![Thing {
a: "asd".to_string(), a: "asd".to_string(),
b: 10, b: 10,
}] }]
}); });
let things = use_ref(&cx, || { let things = use_ref(cx, || {
vec![Thing { vec![Thing {
a: "asd".to_string(), a: "asd".to_string(),
b: 10, b: 10,
@@ -28,7 +28,7 @@ fn example(cx: Scope) -> Element {
}); });
let things_list = things.read(); let things_list = things.read();
let mything = use_ref(&cx, || Some(String::from("asd"))); let mything = use_ref(cx, || Some(String::from("asd")));
let mything_read = mything.read(); let mything_read = mything.read();
cx.render(rsx!( cx.render(rsx!(
+1 -1
View File
@@ -200,7 +200,7 @@ fn app(cx: Scope) -> Element {
// helper functions // helper functions
// Anything that implements IntoVnode can be dropped directly into Rsx // Anything that implements IntoVnode can be dropped directly into Rsx
helper(&cx, "hello world!") helper(cx, "hello world!")
// Strings can be supplied directly // Strings can be supplied directly
String::from("Hello world!") String::from("Hello world!")
+5 -13
View File
@@ -2,16 +2,13 @@
//! //!
//! This example shows how we can render the Dioxus Virtualdom using SSR. //! This example shows how we can render the Dioxus Virtualdom using SSR.
use std::fmt::Write;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_ssr::config::Config;
fn main() { fn main() {
// We can render VirtualDoms // We can render VirtualDoms
let mut vdom = VirtualDom::new(app); let mut vdom = VirtualDom::new(app);
let _ = vdom.rebuild(); let _ = vdom.rebuild();
println!("{}", dioxus_ssr::render_vdom(&vdom)); println!("{}", dioxus_ssr::render(&vdom));
// Or we can render rsx! calls themselves // Or we can render rsx! calls themselves
println!( println!(
@@ -24,17 +21,12 @@ fn main() {
); );
// We can configure the SSR rendering to add ids for rehydration // We can configure the SSR rendering to add ids for rehydration
println!( println!("{}", dioxus_ssr::pre_render(&vdom));
"{}",
dioxus_ssr::render_vdom_cfg(&vdom, Config::default().pre_render(true))
);
// We can even render as a writer // We can render to a buf directly too
let mut file = String::new(); let mut file = String::new();
let _ = file.write_fmt(format_args!( let mut renderer = dioxus_ssr::Renderer::default();
"{}", renderer.render_to(&mut file, &vdom).unwrap();
dioxus_ssr::SsrRender::default().render_vdom(&vdom)
));
println!("{}", file); println!("{}", file);
} }
+1 -1
View File
@@ -53,7 +53,7 @@ fn app(cx: Scope) -> Element {
/// Suspense is achieved my moving the future into only the component that /// Suspense is achieved my moving the future into only the component that
/// actually renders the data. /// actually renders the data.
fn Doggo(cx: Scope) -> Element { fn Doggo(cx: Scope) -> Element {
let fut = use_future(&cx, (), |_| async move { let fut = use_future(cx, (), |_| async move {
reqwest::get("https://dog.ceo/api/breeds/image/random/") reqwest::get("https://dog.ceo/api/breeds/image/random/")
.await .await
.unwrap() .unwrap()
+1 -1
View File
@@ -7,7 +7,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let val = use_state(&cx, || 5); let val = use_state(cx, || 5);
cx.render(rsx! { cx.render(rsx! {
div { div {
+2 -2
View File
@@ -10,9 +10,9 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let count = use_state(&cx, || 0); let count = use_state(cx, || 0);
use_future(&cx, (), move |_| { use_future(cx, (), move |_| {
let mut count = count.clone(); let mut count = count.clone();
async move { async move {
loop { loop {
+1 -1
View File
@@ -7,7 +7,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let model = use_state(&cx, || String::from("asd")); let model = use_state(cx, || String::from("asd"));
println!("{}", model); println!("{}", model);
+4 -4
View File
@@ -12,15 +12,15 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let window = dioxus_desktop::use_window(&cx); let window = dioxus_desktop::use_window(cx);
// if you want to make window fullscreen, you need close the resizable. // if you want to make window fullscreen, you need close the resizable.
// window.set_fullscreen(true); // window.set_fullscreen(true);
// window.set_resizable(false); // window.set_resizable(false);
let fullscreen = use_state(&cx, || false); let fullscreen = use_state(cx, || false);
let always_on_top = use_state(&cx, || false); let always_on_top = use_state(cx, || false);
let decorations = use_state(&cx, || false); let decorations = use_state(cx, || false);
cx.render(rsx!( cx.render(rsx!(
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" } link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" }
+2 -2
View File
@@ -6,9 +6,9 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let window = use_window(&cx); let window = use_window(cx);
let level = use_state(&cx, || 1.0); let level = use_state(cx, || 1.0);
cx.render(rsx! { cx.render(rsx! {
input { input {
r#type: "number", r#type: "number",
+1 -1
View File
@@ -9,7 +9,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let contents = use_state(&cx, || { let contents = use_state(cx, || {
String::from("<script>alert(\"hello world\")</script>") String::from("<script>alert(\"hello world\")</script>")
}); });
+2 -2
View File
@@ -233,7 +233,7 @@ use hooks to define state and modify it from within listeners.
```rust, ignore ```rust, ignore
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let name = use_state(&cx, || "world"); let name = use_state(cx, || "world");
render!("hello {name}!") render!("hello {name}!")
} }
@@ -280,7 +280,7 @@ fn main() {
} }
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
let count = use_state(&cx, || 0); let count = use_state(cx, || 0);
cx.render(rsx!( cx.render(rsx!(
div { "Count: {count}" } div { "Count: {count}" }
+1 -1
View File
@@ -54,7 +54,7 @@ Dioxus 是一个可移植的、高性能的、符合人体工程学的框架,
```rust ```rust
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
h1 { "High-Five counter: {count}" } h1 { "High-Five counter: {count}" }
+2 -2
View File
@@ -406,8 +406,8 @@ enum Patch {
``` ```
```rust ```rust
let node_ref = use_node_ref(&cx); let node_ref = use_node_ref(cx);
use_effect(&cx, || { use_effect(cx, || {
}, []); }, []);
div { ref: node_ref, div { ref: node_ref,
+2 -1
View File
@@ -21,7 +21,8 @@ use std::{
/// } /// }
/// ``` /// ```
pub struct Event<T: 'static + ?Sized> { pub struct Event<T: 'static + ?Sized> {
pub(crate) data: Rc<T>, /// The data associated with this event
pub data: Rc<T>,
pub(crate) propogates: Rc<Cell<bool>>, pub(crate) propogates: Rc<Cell<bool>>,
} }
+8
View File
@@ -514,6 +514,14 @@ impl<'a> IntoTemplate<'a> for VNode<'a> {
self self
} }
} }
impl<'a> IntoTemplate<'a> for Element<'a> {
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> {
match self {
Ok(val) => val.into_template(_cx),
_ => VNode::empty().unwrap(),
}
}
}
impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> { impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> {
fn into_template(self, cx: &'a ScopeState) -> VNode<'a> { fn into_template(self, cx: &'a ScopeState) -> VNode<'a> {
self.call(cx) self.call(cx)
+5 -2
View File
@@ -21,9 +21,12 @@ pub fn use_window(cx: &ScopeState) -> &DesktopContext {
/// Get a closure that executes any JavaScript in the WebView context. /// Get a closure that executes any JavaScript in the WebView context.
pub fn use_eval(cx: &ScopeState) -> &Rc<dyn Fn(String)> { pub fn use_eval(cx: &ScopeState) -> &Rc<dyn Fn(String)> {
let desktop = use_window(cx).clone(); let desktop = use_window(cx);
&*cx.use_hook(|| Rc::new(move |script| desktop.eval(script)) as Rc<dyn Fn(String)>) &*cx.use_hook(|| {
let desktop = desktop.clone();
Rc::new(move |script| desktop.eval(script))
} as Rc<dyn Fn(String)>)
} }
/// An imperative interface to the current window. /// An imperative interface to the current window.
+8 -8
View File
@@ -4,26 +4,26 @@ use rand::prelude::*;
fn main() { fn main() {
let mut dom = VirtualDom::new(app); let mut dom = VirtualDom::new(app);
dom.rebuild(); _ = dom.rebuild();
for _ in 0..1000 { for _ in 0..1000 {
dom.rebuild(); _ = dom.rebuild();
} }
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let mut rng = SmallRng::from_entropy(); let mut rng = SmallRng::from_entropy();
render! ( cx.render(rsx! (
table { table {
tbody { tbody {
(0..10_000_usize).map(|f| { (0..10_000_usize).map(|f| {
let label = Label::new(&mut rng); let label = Label::new(&mut rng);
rsx!( Row { row_id: f, label: label } ) rsx!( table_row { row_id: f, label: label } )
}) })
} }
} }
) ))
} }
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
@@ -31,9 +31,9 @@ struct RowProps {
row_id: usize, row_id: usize,
label: Label, label: Label,
} }
fn Row(cx: Scope<RowProps>) -> Element { fn table_row(cx: Scope<RowProps>) -> Element {
let [adj, col, noun] = cx.props.label.0; let [adj, col, noun] = cx.props.label.0;
render! { cx.render(rsx! {
tr { tr {
td { class:"col-md-1", "{cx.props.row_id}" } td { class:"col-md-1", "{cx.props.row_id}" }
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }, td { class:"col-md-1", onclick: move |_| { /* run onselect */ },
@@ -46,7 +46,7 @@ fn Row(cx: Scope<RowProps>) -> Element {
} }
td { class: "col-md-6" } td { class: "col-md-6" }
} }
} })
} }
#[derive(PartialEq)] #[derive(PartialEq)]
+2 -2
View File
@@ -44,7 +44,7 @@ From anywhere in our app, we can read our the value of our atom:
```rust, ignore ```rust, ignore
fn NameCard(cx: Scope) -> Element { fn NameCard(cx: Scope) -> Element {
let name = use_read(&cx, NAME); let name = use_read(cx, NAME);
cx.render(rsx!{ h1 { "Hello, {name}"} }) cx.render(rsx!{ h1 { "Hello, {name}"} })
} }
``` ```
@@ -53,7 +53,7 @@ We can also set the value of our atom, also from anywhere in our app:
```rust, ignore ```rust, ignore
fn NameCard(cx: Scope) -> Element { fn NameCard(cx: Scope) -> Element {
let set_name = use_set(&cx, NAME); let set_name = use_set(cx, NAME);
cx.render(rsx!{ cx.render(rsx!{
button { button {
onclick: move |_| set_name("Fermi"), onclick: move |_| set_name("Fermi"),
+8 -8
View File
@@ -19,7 +19,7 @@ use std::{
/// static COUNT: Atom<u32> = |_| 0; /// static COUNT: Atom<u32> = |_| 0;
/// ///
/// fn Example(cx: Scope) -> Element { /// fn Example(cx: Scope) -> Element {
/// let mut count = use_atom_state(&cx, COUNT); /// let mut count = use_atom_state(cx, COUNT);
/// ///
/// cx.render(rsx! { /// cx.render(rsx! {
/// div { /// div {
@@ -74,7 +74,7 @@ impl<T: 'static> AtomState<T> {
/// ///
/// ```rust, ignore /// ```rust, ignore
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let count = use_state(&cx, || 0); /// let count = use_state(cx, || 0);
/// cx.spawn({ /// cx.spawn({
/// let set_count = count.to_owned(); /// let set_count = count.to_owned();
/// async move { /// async move {
@@ -101,7 +101,7 @@ impl<T: 'static> AtomState<T> {
/// ///
/// ```rust, ignore /// ```rust, ignore
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let value = use_state(&cx, || 0); /// let value = use_state(cx, || 0);
/// ///
/// rsx!{ /// rsx!{
/// Component { /// Component {
@@ -128,7 +128,7 @@ impl<T: 'static> AtomState<T> {
/// # use dioxus_core::prelude::*; /// # use dioxus_core::prelude::*;
/// # use dioxus_hooks::*; /// # use dioxus_hooks::*;
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let value = use_state(&cx, || 0); /// let value = use_state(cx, || 0);
/// ///
/// // to increment the value /// // to increment the value
/// value.modify(|v| v + 1); /// value.modify(|v| v + 1);
@@ -168,7 +168,7 @@ impl<T: 'static> AtomState<T> {
/// # use dioxus_core::prelude::*; /// # use dioxus_core::prelude::*;
/// # use dioxus_hooks::*; /// # use dioxus_hooks::*;
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let value = use_state(&cx, || 0); /// let value = use_state(cx, || 0);
/// ///
/// let as_rc = value.get(); /// let as_rc = value.get();
/// assert_eq!(as_rc.as_ref(), &0); /// assert_eq!(as_rc.as_ref(), &0);
@@ -190,7 +190,7 @@ impl<T: 'static> AtomState<T> {
/// ///
/// ```rust, ignore /// ```rust, ignore
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let count = use_state(&cx, || 0); /// let count = use_state(cx, || 0);
/// cx.spawn({ /// cx.spawn({
/// let count = count.to_owned(); /// let count = count.to_owned();
/// async move { /// async move {
@@ -220,7 +220,7 @@ impl<T: Clone> AtomState<T> {
/// # Examples /// # Examples
/// ///
/// ```ignore /// ```ignore
/// let val = use_state(&cx, || 0); /// let val = use_state(cx, || 0);
/// ///
/// val.with_mut(|v| *v = 1); /// val.with_mut(|v| *v = 1);
/// ``` /// ```
@@ -243,7 +243,7 @@ impl<T: Clone> AtomState<T> {
/// # Examples /// # Examples
/// ///
/// ```ignore /// ```ignore
/// let val = use_state(&cx, || 0); /// let val = use_state(cx, || 0);
/// ///
/// *val.make_mut() += 1; /// *val.make_mut() += 1;
/// ``` /// ```
+2 -2
View File
@@ -15,8 +15,8 @@ You can always use it "normally" with the `split` method:
```rust ```rust
// Rusty-smart-pointer usage: // Rusty-smart-pointer usage:
let value = use_state(&cx, || 10); let value = use_state(cx, || 10);
// "Classic" usage: // "Classic" usage:
let (value, set_value) = use_state(&cx, || 0).split(); let (value, set_value) = use_state(cx, || 0).split();
``` ```
+3 -3
View File
@@ -5,7 +5,7 @@
/// ///
/// ///
/// ```ignore /// ```ignore
/// let (data) = use_ref(&cx, || {}); /// let (data) = use_ref(cx, || {});
/// ///
/// let handle_thing = move |_| { /// let handle_thing = move |_| {
/// to_owned![data] /// to_owned![data]
@@ -30,8 +30,8 @@ pub use usestate::{use_state, UseState};
mod useref; mod useref;
pub use useref::*; pub use useref::*;
// mod use_shared_state; mod use_shared_state;
// pub use use_shared_state::*; pub use use_shared_state::*;
mod usecoroutine; mod usecoroutine;
pub use usecoroutine::*; pub use usecoroutine::*;
+3 -10
View File
@@ -60,10 +60,10 @@ impl<T> ProvidedStateInner<T> {
/// ///
/// ///
/// ///
pub fn use_context<T: 'static>(cx: &ScopeState) -> Option<UseSharedState<T>> { pub fn use_shared_state<T: 'static>(cx: &ScopeState) -> Option<UseSharedState<T>> {
let state = cx.use_hook(|| { let state = cx.use_hook(|| {
let scope_id = cx.scope_id(); let scope_id = cx.scope_id();
let root = cx.consume_context::<ProvidedState<T>>().cloned(); let root = cx.consume_context::<ProvidedState<T>>();
if let Some(root) = root.as_ref() { if let Some(root) = root.as_ref() {
root.borrow_mut().consumers.insert(scope_id); root.borrow_mut().consumers.insert(scope_id);
@@ -165,14 +165,7 @@ where
} }
/// Provide some state for components down the hierarchy to consume without having to drill props. /// Provide some state for components down the hierarchy to consume without having to drill props.
/// pub fn use_shared_state_provider<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) {
///
///
///
///
///
///
pub fn use_context_provider<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) {
cx.use_hook(|| { cx.use_hook(|| {
let state: ProvidedState<T> = Rc::new(RefCell::new(ProvidedStateInner { let state: ProvidedState<T> = Rc::new(RefCell::new(ProvidedStateInner {
value: Rc::new(RefCell::new(f())), value: Rc::new(RefCell::new(f())),
+1 -1
View File
@@ -16,7 +16,7 @@ This is moderately efficient because the fields of the map are moved, but the da
However, if you used similar approach with Dioxus: However, if you used similar approach with Dioxus:
```rust ```rust
let (map, set_map) = use_state(&cx, || HashMap::new()); let (map, set_map) = use_state(cx, || HashMap::new());
set_map({ set_map({
let mut newmap = map.clone(); let mut newmap = map.clone();
newmap.set(key, value); newmap.set(key, value);
+2
View File
@@ -1,6 +1,8 @@
use dioxus_core::ScopeState; use dioxus_core::ScopeState;
/// Consume some context in the tree, providing a sharable handle to the value /// Consume some context in the tree, providing a sharable handle to the value
///
/// Does not regenerate the value if the value is changed at the parent.
pub fn use_context<T: 'static + Clone>(cx: &ScopeState) -> Option<&T> { pub fn use_context<T: 'static + Clone>(cx: &ScopeState) -> Option<&T> {
cx.use_hook(|| cx.consume_context::<T>()).as_ref() cx.use_hook(|| cx.consume_context::<T>()).as_ref()
} }
+8 -9
View File
@@ -2,8 +2,6 @@ use dioxus_core::{ScopeState, TaskId};
pub use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; pub use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
use std::future::Future; use std::future::Future;
use crate::{use_context, use_context_provider};
/// Maintain a handle over a future that can be paused, resumed, and canceled. /// Maintain a handle over a future that can be paused, resumed, and canceled.
/// ///
/// This is an upgraded form of [`use_future`] with an integrated channel system. /// This is an upgraded form of [`use_future`] with an integrated channel system.
@@ -44,7 +42,7 @@ use crate::{use_context, use_context_provider};
/// Stop, /// Stop,
/// } /// }
/// ///
/// let chat_client = use_coroutine(&cx, |rx: UnboundedReceiver<Action>| async move { /// let chat_client = use_coroutine(cx, |rx: UnboundedReceiver<Action>| async move {
/// while let Some(action) = rx.next().await { /// while let Some(action) = rx.next().await {
/// match action { /// match action {
/// Action::Start => {} /// Action::Start => {}
@@ -67,10 +65,10 @@ where
G: FnOnce(UnboundedReceiver<M>) -> F, G: FnOnce(UnboundedReceiver<M>) -> F,
F: Future<Output = ()> + 'static, F: Future<Output = ()> + 'static,
{ {
use_context_provider(cx, || { cx.use_hook(|| {
let (tx, rx) = futures_channel::mpsc::unbounded(); let (tx, rx) = futures_channel::mpsc::unbounded();
let task = cx.push_future(init(rx)); let task = cx.push_future(init(rx));
Coroutine { tx, task } cx.provide_context(Coroutine { tx, task })
}) })
} }
@@ -78,7 +76,8 @@ where
/// ///
/// See the docs for [`use_coroutine`] for more details. /// See the docs for [`use_coroutine`] for more details.
pub fn use_coroutine_handle<M: 'static>(cx: &ScopeState) -> Option<&Coroutine<M>> { pub fn use_coroutine_handle<M: 'static>(cx: &ScopeState) -> Option<&Coroutine<M>> {
use_context::<Coroutine<M>>(cx) cx.use_hook(|| cx.consume_context::<Coroutine<M>>())
.as_ref()
} }
pub struct Coroutine<T> { pub struct Coroutine<T> {
@@ -119,15 +118,15 @@ mod tests {
use futures_util::StreamExt; use futures_util::StreamExt;
fn app(cx: Scope, name: String) -> Element { fn app(cx: Scope, name: String) -> Element {
let task = use_coroutine(&cx, |mut rx: UnboundedReceiver<i32>| async move { let task = use_coroutine(cx, |mut rx: UnboundedReceiver<i32>| async move {
while let Some(msg) = rx.next().await { while let Some(msg) = rx.next().await {
println!("got message: {}", msg); println!("got message: {}", msg);
} }
}); });
let task2 = use_coroutine(&cx, view_task); let task2 = use_coroutine(cx, view_task);
let task3 = use_coroutine(&cx, |rx| complex_task(rx, 10)); let task3 = use_coroutine(cx, |rx| complex_task(rx, 10));
todo!() todo!()
} }
+4 -4
View File
@@ -17,7 +17,7 @@ use crate::UseFutureDep;
/// ///
/// #[inline_props] /// #[inline_props]
/// fn app(cx: Scope, name: &str) -> Element { /// fn app(cx: Scope, name: &str) -> Element {
/// use_effect(&cx, (name,), |(name,)| async move { /// use_effect(cx, (name,), |(name,)| async move {
/// set_title(name); /// set_title(name);
/// })) /// }))
/// } /// }
@@ -72,17 +72,17 @@ mod tests {
fn app(cx: Scope<MyProps>) -> Element { fn app(cx: Scope<MyProps>) -> Element {
// should only ever run once // should only ever run once
use_effect(&cx, (), |_| async move { use_effect(cx, (), |_| async move {
// //
}); });
// runs when a is changed // runs when a is changed
use_effect(&cx, (&cx.props.a,), |(a,)| async move { use_effect(cx, (&cx.props.a,), |(a,)| async move {
// //
}); });
// runs when a or b is changed // runs when a or b is changed
use_effect(&cx, (&cx.props.a, &cx.props.b), |(a, b)| async move { use_effect(cx, (&cx.props.a, &cx.props.b), |(a, b)| async move {
// //
}); });
+9 -9
View File
@@ -19,7 +19,7 @@ use std::{
/// ///
/// ```ignore /// ```ignore
/// const Example: Component = |cx| { /// const Example: Component = |cx| {
/// let count = use_state(&cx, || 0); /// let count = use_state(cx, || 0);
/// ///
/// cx.render(rsx! { /// cx.render(rsx! {
/// div { /// div {
@@ -92,7 +92,7 @@ impl<T: 'static> UseState<T> {
/// ///
/// ```rust, ignore /// ```rust, ignore
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let count = use_state(&cx, || 0); /// let count = use_state(cx, || 0);
/// cx.spawn({ /// cx.spawn({
/// let set_count = count.to_owned(); /// let set_count = count.to_owned();
/// async move { /// async move {
@@ -119,7 +119,7 @@ impl<T: 'static> UseState<T> {
/// ///
/// ```rust, ignore /// ```rust, ignore
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let value = use_state(&cx, || 0); /// let value = use_state(cx, || 0);
/// ///
/// rsx!{ /// rsx!{
/// Component { /// Component {
@@ -144,7 +144,7 @@ impl<T: 'static> UseState<T> {
/// # use dioxus_core::prelude::*; /// # use dioxus_core::prelude::*;
/// # use dioxus_hooks::*; /// # use dioxus_hooks::*;
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let value = use_state(&cx, || 0); /// let value = use_state(cx, || 0);
/// ///
/// // to increment the value /// // to increment the value
/// value.modify(|v| v + 1); /// value.modify(|v| v + 1);
@@ -185,7 +185,7 @@ impl<T: 'static> UseState<T> {
/// # use dioxus_core::prelude::*; /// # use dioxus_core::prelude::*;
/// # use dioxus_hooks::*; /// # use dioxus_hooks::*;
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let value = use_state(&cx, || 0); /// let value = use_state(cx, || 0);
/// ///
/// let as_rc = value.get(); /// let as_rc = value.get();
/// assert_eq!(as_rc.as_ref(), &0); /// assert_eq!(as_rc.as_ref(), &0);
@@ -207,7 +207,7 @@ impl<T: 'static> UseState<T> {
/// ///
/// ```rust, ignore /// ```rust, ignore
/// fn component(cx: Scope) -> Element { /// fn component(cx: Scope) -> Element {
/// let count = use_state(&cx, || 0); /// let count = use_state(cx, || 0);
/// cx.spawn({ /// cx.spawn({
/// let count = count.to_owned(); /// let count = count.to_owned();
/// async move { /// async move {
@@ -237,7 +237,7 @@ impl<T: Clone> UseState<T> {
/// # Examples /// # Examples
/// ///
/// ```rust, ignore /// ```rust, ignore
/// let val = use_state(&cx, || 0); /// let val = use_state(cx, || 0);
/// ///
/// val.with_mut(|v| *v = 1); /// val.with_mut(|v| *v = 1);
/// ``` /// ```
@@ -269,7 +269,7 @@ impl<T: Clone> UseState<T> {
/// # Examples /// # Examples
/// ///
/// ```rust, ignore /// ```rust, ignore
/// let val = use_state(&cx, || 0); /// let val = use_state(cx, || 0);
/// ///
/// *val.make_mut() += 1; /// *val.make_mut() += 1;
/// ``` /// ```
@@ -448,7 +448,7 @@ impl<T: Div<Output = T> + Copy> std::ops::DivAssign<T> for UseState<T> {
fn api_makes_sense() { fn api_makes_sense() {
#[allow(unused)] #[allow(unused)]
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let val = use_state(&cx, || 0); let val = use_state(cx, || 0);
val.set(0); val.set(0);
val.modify(|v| v + 1); val.modify(|v| v + 1);
-3
View File
@@ -1,6 +1,3 @@
mod keys;
pub use keys::*;
macro_rules! impl_event { macro_rules! impl_event {
( (
$data:ty; $data:ty;
-370
View File
@@ -1,370 +0,0 @@
#[cfg_attr(
feature = "serialize",
derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum KeyCode {
// That key has no keycode, = 0
// break, = 3
// backspace / delete, = 8
// tab, = 9
// clear, = 12
// enter, = 13
// shift, = 16
// ctrl, = 17
// alt, = 18
// pause/break, = 19
// caps lock, = 20
// hangul, = 21
// hanja, = 25
// escape, = 27
// conversion, = 28
// non-conversion, = 29
// spacebar, = 32
// page up, = 33
// page down, = 34
// end, = 35
// home, = 36
// left arrow, = 37
// up arrow, = 38
// right arrow, = 39
// down arrow, = 40
// select, = 41
// print, = 42
// execute, = 43
// Print Screen, = 44
// insert, = 45
// delete, = 46
// help, = 47
// 0, = 48
// 1, = 49
// 2, = 50
// 3, = 51
// 4, = 52
// 5, = 53
// 6, = 54
// 7, = 55
// 8, = 56
// 9, = 57
// :, = 58
// semicolon (firefox), equals, = 59
// <, = 60
// equals (firefox), = 61
// ß, = 63
// @ (firefox), = 64
// a, = 65
// b, = 66
// c, = 67
// d, = 68
// e, = 69
// f, = 70
// g, = 71
// h, = 72
// i, = 73
// j, = 74
// k, = 75
// l, = 76
// m, = 77
// n, = 78
// o, = 79
// p, = 80
// q, = 81
// r, = 82
// s, = 83
// t, = 84
// u, = 85
// v, = 86
// w, = 87
// x, = 88
// y, = 89
// z, = 90
// Windows Key / Left ⌘ / Chromebook Search key, = 91
// right window key, = 92
// Windows Menu / Right ⌘, = 93
// sleep, = 95
// numpad 0, = 96
// numpad 1, = 97
// numpad 2, = 98
// numpad 3, = 99
// numpad 4, = 100
// numpad 5, = 101
// numpad 6, = 102
// numpad 7, = 103
// numpad 8, = 104
// numpad 9, = 105
// multiply, = 106
// add, = 107
// numpad period (firefox), = 108
// subtract, = 109
// decimal point, = 110
// divide, = 111
// f1, = 112
// f2, = 113
// f3, = 114
// f4, = 115
// f5, = 116
// f6, = 117
// f7, = 118
// f8, = 119
// f9, = 120
// f10, = 121
// f11, = 122
// f12, = 123
// f13, = 124
// f14, = 125
// f15, = 126
// f16, = 127
// f17, = 128
// f18, = 129
// f19, = 130
// f20, = 131
// f21, = 132
// f22, = 133
// f23, = 134
// f24, = 135
// f25, = 136
// f26, = 137
// f27, = 138
// f28, = 139
// f29, = 140
// f30, = 141
// f31, = 142
// f32, = 143
// num lock, = 144
// scroll lock, = 145
// airplane mode, = 151
// ^, = 160
// !, = 161
// ؛ (arabic semicolon), = 162
// #, = 163
// $, = 164
// ù, = 165
// page backward, = 166
// page forward, = 167
// refresh, = 168
// closing paren (AZERTY), = 169
// *, = 170
// ~ + * key, = 171
// home key, = 172
// minus (firefox), mute/unmute, = 173
// decrease volume level, = 174
// increase volume level, = 175
// next, = 176
// previous, = 177
// stop, = 178
// play/pause, = 179
// e-mail, = 180
// mute/unmute (firefox), = 181
// decrease volume level (firefox), = 182
// increase volume level (firefox), = 183
// semi-colon / ñ, = 186
// equal sign, = 187
// comma, = 188
// dash, = 189
// period, = 190
// forward slash / ç, = 191
// grave accent / ñ / æ / ö, = 192
// ?, / or °, = 193
// numpad period (chrome), = 194
// open bracket, = 219
// back slash, = 220
// close bracket / å, = 221
// single quote / ø / ä, = 222
// `, = 223
// left or right ⌘ key (firefox), = 224
// altgr, = 225
// < /git >, left back slash, = 226
// GNOME Compose Key, = 230
// ç, = 231
// XF86Forward, = 233
// XF86Back, = 234
// non-conversion, = 235
// alphanumeric, = 240
// hiragana/katakana, = 242
// half-width/full-width, = 243
// kanji, = 244
// unlock trackpad (Chrome/Edge), = 251
// toggle touchpad, = 255
NA = 0,
Break = 3,
Backspace = 8,
Tab = 9,
Clear = 12,
Enter = 13,
Shift = 16,
Ctrl = 17,
Alt = 18,
Pause = 19,
CapsLock = 20,
// hangul, = 21
// hanja, = 25
Escape = 27,
// conversion, = 28
// non-conversion, = 29
Space = 32,
PageUp = 33,
PageDown = 34,
End = 35,
Home = 36,
LeftArrow = 37,
UpArrow = 38,
RightArrow = 39,
DownArrow = 40,
// select, = 41
// print, = 42
// execute, = 43
// Print Screen, = 44
Insert = 45,
Delete = 46,
// help, = 47
Num0 = 48,
Num1 = 49,
Num2 = 50,
Num3 = 51,
Num4 = 52,
Num5 = 53,
Num6 = 54,
Num7 = 55,
Num8 = 56,
Num9 = 57,
// :, = 58
// semicolon (firefox), equals, = 59
// <, = 60
// equals (firefox), = 61
// ß, = 63
// @ (firefox), = 64
A = 65,
B = 66,
C = 67,
D = 68,
E = 69,
F = 70,
G = 71,
H = 72,
I = 73,
J = 74,
K = 75,
L = 76,
M = 77,
N = 78,
O = 79,
P = 80,
Q = 81,
R = 82,
S = 83,
T = 84,
U = 85,
V = 86,
W = 87,
X = 88,
Y = 89,
Z = 90,
LeftWindow = 91,
RightWindow = 92,
SelectKey = 93,
Numpad0 = 96,
Numpad1 = 97,
Numpad2 = 98,
Numpad3 = 99,
Numpad4 = 100,
Numpad5 = 101,
Numpad6 = 102,
Numpad7 = 103,
Numpad8 = 104,
Numpad9 = 105,
Multiply = 106,
Add = 107,
Subtract = 109,
DecimalPoint = 110,
Divide = 111,
F1 = 112,
F2 = 113,
F3 = 114,
F4 = 115,
F5 = 116,
F6 = 117,
F7 = 118,
F8 = 119,
F9 = 120,
F10 = 121,
F11 = 122,
F12 = 123,
// f13, = 124
// f14, = 125
// f15, = 126
// f16, = 127
// f17, = 128
// f18, = 129
// f19, = 130
// f20, = 131
// f21, = 132
// f22, = 133
// f23, = 134
// f24, = 135
// f25, = 136
// f26, = 137
// f27, = 138
// f28, = 139
// f29, = 140
// f30, = 141
// f31, = 142
// f32, = 143
NumLock = 144,
ScrollLock = 145,
// airplane mode, = 151
// ^, = 160
// !, = 161
// ؛ (arabic semicolon), = 162
// #, = 163
// $, = 164
// ù, = 165
// page backward, = 166
// page forward, = 167
// refresh, = 168
// closing paren (AZERTY), = 169
// *, = 170
// ~ + * key, = 171
// home key, = 172
// minus (firefox), mute/unmute, = 173
// decrease volume level, = 174
// increase volume level, = 175
// next, = 176
// previous, = 177
// stop, = 178
// play/pause, = 179
// e-mail, = 180
// mute/unmute (firefox), = 181
// decrease volume level (firefox), = 182
// increase volume level (firefox), = 183
Semicolon = 186,
EqualSign = 187,
Comma = 188,
Dash = 189,
Period = 190,
ForwardSlash = 191,
GraveAccent = 192,
// ?, / or °, = 193
// numpad period (chrome), = 194
OpenBracket = 219,
BackSlash = 220,
CloseBraket = 221,
SingleQuote = 222,
// `, = 223
// left or right ⌘ key (firefox), = 224
// altgr, = 225
// < /git >, left back slash, = 226
// GNOME Compose Key, = 230
// ç, = 231
// XF86Forward, = 233
// XF86Back, = 234
// non-conversion, = 235
// alphanumeric, = 240
// hiragana/katakana, = 242
// half-width/full-width, = 243
// kanji, = 244
// unlock trackpad (Chrome/Edge), = 251
// toggle touchpad, = 255
#[cfg_attr(feature = "serialize", serde(other))]
Unknown,
}
+2 -2
View File
@@ -2,7 +2,7 @@
use js_sys::Function; use js_sys::Function;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use web_sys::{Element, Node}; use web_sys::Element;
#[wasm_bindgen(module = "/src/interpreter.js")] #[wasm_bindgen(module = "/src/interpreter.js")]
extern "C" { extern "C" {
@@ -18,7 +18,7 @@ extern "C" {
pub fn MountToRoot(this: &Interpreter); pub fn MountToRoot(this: &Interpreter);
#[wasm_bindgen(method)] #[wasm_bindgen(method)]
pub fn AppendChildren(this: &Interpreter, m: u32); pub fn AppendChildren(this: &Interpreter, m: u32, id: u32);
#[wasm_bindgen(method)] #[wasm_bindgen(method)]
pub fn AssignId(this: &Interpreter, path: &[u8], id: u32); pub fn AssignId(this: &Interpreter, path: &[u8], id: u32);
+1 -1
View File
@@ -40,7 +40,7 @@ async fn order_shoes(mut req: WebsocketRequest) -> Response {
} }
fn App(cx: Scope) -> Element { fn App(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx!( cx.render(rsx!(
button { onclick: move |_| count += 1, "Incr" } button { onclick: move |_| count += 1, "Incr" }
button { onclick: move |_| count -= 1, "Decr" } button { onclick: move |_| count -= 1, "Decr" }
+2 -2
View File
@@ -6,7 +6,7 @@ use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use dioxus_core::ElementId; use dioxus_core::ElementId;
use dioxus_html::event_bubbles; // use dioxus_html::event_bubbles;
use dioxus_html::events::*; use dioxus_html::events::*;
#[derive(serde::Serialize, serde::Deserialize)] #[derive(serde::Serialize, serde::Deserialize)]
@@ -29,7 +29,7 @@ struct ImEvent {
contents: serde_json::Value, contents: serde_json::Value,
} }
pub fn trigger_from_serialized(val: serde_json::Value) -> () { pub fn trigger_from_serialized(_val: serde_json::Value) {
todo!() todo!()
// let ImEvent { // let ImEvent {
// event, // event,
@@ -1,4 +1,3 @@
use anymap::AnyMap;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_native_core::node_ref::*; use dioxus_native_core::node_ref::*;
use dioxus_native_core::real_dom::*; use dioxus_native_core::real_dom::*;
@@ -1,10 +1,8 @@
use dioxus::core::ElementId;
use dioxus::core::{AttributeValue, Mutations};
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_native_core::real_dom::*; use dioxus_native_core::real_dom::*;
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
use dioxus_native_core::tree::TreeView; use dioxus_native_core::tree::TreeView;
use dioxus_native_core::{node_ref::*, NodeId, RealNodeId, SendAnyMap}; use dioxus_native_core::{node_ref::*, NodeId, SendAnyMap};
use dioxus_native_core_macro::State; use dioxus_native_core_macro::State;
#[derive(Debug, Clone, Default, State)] #[derive(Debug, Clone, Default, State)]
+3 -1
View File
@@ -36,7 +36,9 @@ fn BlogList(cx: Scope) -> Element {
} }
fn BlogPost(cx: Scope) -> Element { fn BlogPost(cx: Scope) -> Element {
let id = use_route(&cx).segment("id")?; let Some(id) = use_route(cx).segment("id") else {
return cx.render(rsx! { div { "No blog post id" } })
};
log::trace!("rendering blog post {}", id); log::trace!("rendering blog post {}", id);
+3 -3
View File
@@ -39,15 +39,15 @@ pub struct RouterProps<'a> {
/// Will fallback to HashRouter is BrowserRouter is not available, or through configuration. /// Will fallback to HashRouter is BrowserRouter is not available, or through configuration.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element { pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
let svc = use_context_provider(cx, || { let svc = cx.use_hook(|| {
RouterService::new( cx.provide_context(RouterService::new(
cx, cx,
RouterCfg { RouterCfg {
base_url: cx.props.base_url.map(|s| s.to_string()), base_url: cx.props.base_url.map(|s| s.to_string()),
active_class: cx.props.active_class.map(|s| s.to_string()), active_class: cx.props.active_class.map(|s| s.to_string()),
initial_url: cx.props.initial_url.clone(), initial_url: cx.props.initial_url.clone(),
}, },
) ))
}); });
// next time we run the rout_found will be filled // next time we run the rout_found will be filled
+1 -1
View File
@@ -415,7 +415,7 @@ mod web {
} }
} }
fn attach_listeners(&self, svc: std::sync::Weak<crate::RouterService>) { fn attach_listeners(&self, svc: std::rc::Weak<crate::RouterService>) {
self._listener.set(Some(EventListener::new( self._listener.set(Some(EventListener::new(
&web_sys::window().unwrap(), &web_sys::window().unwrap(),
"popstate", "popstate",
+6 -8
View File
@@ -13,13 +13,13 @@ fn simple_test() {
fn main() { fn main() {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
dioxus_web::launch(APP); dioxus_web::launch(app);
} }
static APP: Component = |cx| { fn app(cx: Scope) -> Element {
cx.render(rsx! { cx.render(rsx! {
Router { Router {
onchange: move |route: RouterService| log::trace!("route changed to {:?}", route.current_location()), onchange: move |router: RouterContext| log::trace!("route changed to {:?}", router.current_location()),
active_class: "is-active", active_class: "is-active",
Route { to: "/", Home {} } Route { to: "/", Home {} }
Route { to: "blog" Route { to: "blog"
@@ -28,7 +28,7 @@ fn simple_test() {
} }
} }
}) })
}; }
fn Home(cx: Scope) -> Element { fn Home(cx: Scope) -> Element {
cx.render(rsx! { cx.render(rsx! {
@@ -47,12 +47,10 @@ fn simple_test() {
} }
fn BlogPost(cx: Scope) -> Element { fn BlogPost(cx: Scope) -> Element {
let _id = use_route(&cx).parse_segment::<usize>("id")?; let _id = use_route(cx).parse_segment::<usize>("id").unwrap();
cx.render(rsx! { cx.render(rsx! {
div { div { }
}
}) })
} }
+3 -3
View File
@@ -109,7 +109,7 @@ For any route, you can get a handle the current route with the `use_route` hook.
```rust ```rust
fn Title(cx: Scope) -> Element { fn Title(cx: Scope) -> Element {
let route = use_route(&cx); let route = use_route(cx);
assert_eq!(route.segments(), &["dogs", "breeds", "yorkie", "hugo"]); assert_eq!(route.segments(), &["dogs", "breeds", "yorkie", "hugo"]);
@@ -130,7 +130,7 @@ Router {
} }
fn BlogPost(cx: Scope) -> Element { fn BlogPost(cx: Scope) -> Element {
let route = use_route(&cx); let route = use_route(cx);
match route.segment("post").and_then(parse) { match route.segment("post").and_then(parse) {
Some(post) => cx.render(rsx!{ div { "Post {post}" } }) Some(post) => cx.render(rsx!{ div { "Post {post}" } })
@@ -168,7 +168,7 @@ Listeners can also be attached downstream in your app with the `RouteListener` h
```rust ```rust
fn TitleCard(cx: Scope) -> Element { fn TitleCard(cx: Scope) -> Element {
let (title, set_title) = use_state(&cx, || "First"); let (title, set_title) = use_state(cx, || "First");
cx.render(rsx!{ cx.render(rsx!{
h1 { "render {title}" } h1 { "render {title}" }
+4 -4
View File
@@ -192,16 +192,16 @@ impl ToTokens for Element {
#[derive(PartialEq, Eq, Clone, Debug, Hash)] #[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ElementAttr { pub enum ElementAttr {
/// attribute: "valuee {}" /// `attribute: "value"`
AttrText { name: Ident, value: IfmtInput }, AttrText { name: Ident, value: IfmtInput },
/// attribute: true, /// `attribute: true`
AttrExpression { name: Ident, value: Expr }, AttrExpression { name: Ident, value: Expr },
/// "attribute": "value {}" /// `"attribute": "value"`
CustomAttrText { name: LitStr, value: IfmtInput }, CustomAttrText { name: LitStr, value: IfmtInput },
/// "attribute": true, /// `"attribute": true`
CustomAttrExpression { name: LitStr, value: Expr }, CustomAttrExpression { name: LitStr, value: Expr },
// /// onclick: move |_| {} // /// onclick: move |_| {}
+2 -2
View File
@@ -199,8 +199,8 @@ impl<'a> DynamicContext<'a> {
let value = value.source.as_ref().unwrap(); let value = value.source.as_ref().unwrap();
quote! { quote! {
::dioxus::core::TemplateAttribute::Static { ::dioxus::core::TemplateAttribute::Static {
name: dioxus_elements::#el_name::#name.0, name: #name,
namespace: dioxus_elements::#el_name::#name.1, namespace: None,
value: #value, value: #value,
// todo: we don't diff these so we never apply the volatile flag // todo: we don't diff these so we never apply the volatile flag
+1
View File
@@ -1,6 +1,7 @@
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
use std::fmt::Write; use std::fmt::Write;
#[derive(Debug)]
pub struct StringCache { pub struct StringCache {
pub segments: Vec<Segment>, pub segments: Vec<Segment>,
pub template: Template<'static>, pub template: Template<'static>,
+22 -19
View File
@@ -156,26 +156,29 @@ fn to_string_works() {
_ = dom.rebuild(); _ = dom.rebuild();
let mut renderer = Renderer::new(); let mut renderer = Renderer::new();
let out = renderer.render(&dom); let out = renderer.render(&dom);
use Segment::*; for item in renderer.template_cache.iter() {
assert_eq!( if item.1.segments.len() > 5 {
renderer.template_cache.iter().next().unwrap().1.segments, assert_eq!(
vec![ item.1.segments,
PreRendered("<div class=\"asdasdasd\" class=\"asdasdasd\"".into(),), vec![
Attr(0,), PreRendered("<div class=\"asdasdasd\" class=\"asdasdasd\"".into(),),
PreRendered(">Hello world 1 -->".into(),), Attr(0,),
Node(0,), PreRendered(">Hello world 1 -->".into(),),
PreRendered("<-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),), Node(0,),
Node(1,), PreRendered(
Node(2,), "<-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),
PreRendered("</div>".into(),), ),
] Node(1,),
); Node(2,),
PreRendered("</div>".into(),),
]
);
}
}
assert_eq!( use Segment::*;
out,
"<div class=\"asdasdasd\" class=\"asdasdasd\" id=\"id-123\">Hello world 1 --><!--#-->123<!--/#--><-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div><!--#--></diiiiiiiiv><!--/#--><div><!--#-->finalize 0<!--/#--></div><div><!--#-->finalize 1<!--/#--></div><div><!--#-->finalize 2<!--/#--></div><div><!--#-->finalize 3<!--/#--></div><div><!--#-->finalize 4<!--/#--></div></div>" assert_eq!(out, "<div class=\"asdasdasd\" class=\"asdasdasd\" id=\"id-123\">Hello world 1 -->123<-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div></diiiiiiiiv><div>finalize 0</div><div>finalize 1</div><div>finalize 2</div><div>finalize 3</div><div>finalize 4</div></div>");
);
} }
+3 -3
View File
@@ -41,7 +41,7 @@ struct BoxProps {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn Box(cx: Scope<BoxProps>) -> Element { fn Box(cx: Scope<BoxProps>) -> Element {
let count = use_state(&cx, || 0); let count = use_state(cx, || 0);
let x = cx.props.x * 2; let x = cx.props.x * 2;
let y = cx.props.y * 2; let y = cx.props.y * 2;
@@ -70,8 +70,8 @@ struct GridProps {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn Grid(cx: Scope<GridProps>) -> Element { fn Grid(cx: Scope<GridProps>) -> Element {
let size = cx.props.size; let size = cx.props.size;
let count = use_state(&cx, || 0); let count = use_state(cx, || 0);
let counts = use_ref(&cx, || vec![0; size * size]); let counts = use_ref(cx, || vec![0; size * size]);
let ctx: &TuiContext = cx.consume_context().unwrap(); let ctx: &TuiContext = cx.consume_context().unwrap();
if *count.get() + 1 >= (size * size) { if *count.get() + 1 >= (size * size) {
+1 -1
View File
@@ -27,7 +27,7 @@ enum Event {
const MAX_EVENTS: usize = 8; const MAX_EVENTS: usize = 8;
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let events = use_ref(&cx, Vec::new); let events = use_ref(cx, Vec::new);
let events_lock = events.read(); let events_lock = events.read();
let first_index = events_lock.len().saturating_sub(MAX_EVENTS); let first_index = events_lock.len().saturating_sub(MAX_EVENTS);
+1 -1
View File
@@ -5,7 +5,7 @@ fn main() {
} }
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let radius = use_state(&cx, || 0); let radius = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
div { div {
Loaded 100 of 113 files, more files were not shown because too many files have changed in this diff. Show more