--hot-reload, instant output!Send so
.await points
https://github.com/DioxusLabs/dioxus/pull/928
Changes
fn main() {
dioxus_web::launch(app);
}
fn app(cx: Scope) -> Element {
cx.render(rsx! {
div {
"Hello, world!"
}
})
}
let count = use_state(cx, || 0);rsx! (like jsx)
</div>
rsx! {
div {
class: "bg-dark",
onclick: |_event| { /* do something */ },
h1 { "Header" }
"Hello, {x}!"
}
}
match! but rsx! integration not that good
(have to use rsx! within match body)"true" and "false"
input {
checked: if active_todo_count == 0 { "true" } else { "false" }
}
UseState no compile-time lifetime check (runtime instead)
make_mut to minimum event handlingmake_mut in main blockondoubleclick is ondblclick? I was wondering why it didn’t
label {
r#for: "cbg-{todo.id}",
ondblclick: move |_| is_editing.set(true),
}
#![allow(non_snake_case)]dioxus_desktop::launch_cfg(
app,
Config::new()
.with_custom_head("<script src=\"https://cdn.tailwindcss.com\"></script>".to_string()),
);
jsx! and took me some time to figure this
# Dioxus.toml
[web.resource]
style = [
"https://cdn.jsdelivr.net/npm/daisyui@2.51.5/dist/full.css",
]
script = [
"https://cdn.tailwindcss.com",
]
cx.render(rsx!{
div {
class: class!(card_body text_center items_center hover(scale_105)),
div {
class: class!(card_title text_sm text_base_content),
cx.props.alias
}
}
})
fn app(cx: Scope) -> Element {
let nth = use_state(cx, || 0);
rsx! {
label_item { nth: nth }
}
...
}
#[derive(Props)]
struct LabelItemProps<'a> {
nth: &'a UseState<usize>,
}
fn label_item<'a>(cx: Scope<'a, LabelItemProps<'a>>) -> Element {
cx.render(rsx! {
button {
class: "btn",
onclick: move |_| *cx.props.nth.make_mut() += 1,
"Emission 1"
}
}
}
for choice in history {
p { "{choice:?}" }
}
\ Compiling dioxus-web 0.3.1 (registry+https://github.com/rust-lang/crates.io-inerror: could not compile `dioxus-demo` due to 2 previous errors
[ERROR] error: expected `,`
--> src/main.rs:118:31
|
118 | ... p { "{choice}" }
| ^
for choice in &history.read() { // correct
p { "{choice:?}" }
}
let todos = use_state(cx, im_rc::HashMap::<u32, TodoItem>::default);
let active_todo_count = todos.values().filter(|item| !item.checked).count();
...
rsx! {
span { class: "todo-count",
strong {"{active_todo_count} "}
span {"{active_todo_text} left"}
}
}
yarn create react-app hello (template)yarn startsrc/App.jsexport default function App() {
return (
<div>
Hello, world!
</div>
);
}
cargo new hellocargo add dioxus dioxus-webdioxus serve --hot-reload (requires cargo install dioxus-cli)src/main.rsuse dioxus::prelude::*;
fn main() {
dioxus_web::launch(app);
}
fn app(cx: Scope) -> Element {
cx.render(rsx! {
div {
"Hello, world!"
}
})
}
To support different platform, change dioxus_web to dioxus_desktop.
If I add
...
let a = 1; // <- this
cx.render(rsx! {
...
Default rust-analyzer and dioxus-cli results in ~27s rebuild.
A fix is to add to .cargo/config (not documented in docs):
[build]
target = "wasm32-unknown-unknown"
Rebuild time now ~1s.

https://jakelazaroff.com/words/were-react-hooks-a-mistake/
Let’s see if dioxus inherit react footguns, even though react say they did.
function CounterButton({ started, count, onClick }) {
return <button onClick={onClick}>{started ? "Current score: " + count : "Start"}</button>;
}
... // a bit long, see next page
class Game extends React.Component {
state = { count: 0, started: false };
increment() {
this.setState({ count: this.state.count + 1 });
}
start() {
if (!this.state.started) setTimeout(() => alert(`Your score was ${this.state.count}!`), 5000);
this.setState({ started: true });
}
render() {
return (
<CounterButton
started={this.state.started}
count={this.state.count}
onClick={() => {
this.increment();
this.start();
}}
/>
);
}
}
const [count, setCount] = useState(0);
const [started, setStarted] = useState(false);
function increment() {
setCount(count + 1);
}
function start() {
if (!started) setTimeout(() => alert(`Your score was ${count}!`), 5000);
setStarted(true);
}
return (
<button
onClick={() => {
increment();
start();
}}
>
{started ? "Current score: " + count : "Start"}
</button>
);
let count = use_state(cx, || 0);
let started = use_state(cx, || false);
let start = || {
cx.spawn({
let has_started = started.to_owned();
let count = count.to_owned(); // this is obvious that variable will not change
started.set(true);
async move {
if !has_started {
let alert = move || gloo_dialogs::alert(&format!("Your score was {count}!"));
gloo_timers::callback::Interval::new(5_000, alert).forget();
}
}
});
};
cx.render(rsx! {
button {
onclick: move |_event| {
*count.make_mut() += 1;
start();
},
// format is needed as {count} does not seemed to work in `if` within content
if **started { format!("Current score: {count}") } else { "Start".to_string() }
}
})
Working backword from correct solution. Need to change mental model.
let count = use_state(cx, || 0);
let started = use_state(cx, || false);
let start = || {
if !*started.get() {
let count = count.clone(); // this is obvious that variable will not change
let alert = move || gloo_dialogs::alert(&format!("Your score was {count}!"));
gloo_timers::callback::Timeout::new(5_000, alert).forget();
}
started.set(true);
};
cx.render(rsx! {
button {
onclick: move |_event| {
start();
*count.make_mut() += 1;
},
// format is needed as {count} does not seemed to work in `if` within content
if **started { format!("Current score: {}", count) } else { "Start".to_string() }
}
})
const [count, setCount] = useState(0);
const [started, setStarted] = useState(false);
const countRef = useRef(count);
function increment() {
setCount(count + 1);
countRef.current = count + 1;
}
function start() {
if (!started) setTimeout(() => alert(`Your score was ${countRef.current}!`), 5000);
setStarted(true);
}
return (
<button
onClick={() => {
increment();
start();
}}
>
{started ? "Current score: " + count : "Start"}
</button>
);
let count = use_ref(cx, || 0); // use_ref
let started = use_state(cx, || false);
let start = || {
if !*started.get() {
let count = count.clone(); // clone reference rather than value
let alert = move || gloo_dialogs::alert(&format!("Your score was {}!", count.read()));
gloo_timers::callback::Timeout::new(5_000, alert).forget();
}
started.set(true);
};
cx.render(rsx! {
button {
onclick: move |_event| {
start();
*count.write() += 1;
},
// format is needed as {count} does not seemed to work in `if` within content
if **started { format!("Current score: {}", count.read()) } else { "Start".to_string() }
}
})
Looks cleaner but took me some time to figure out too.
No compile time error for tokio even when it does not support wasm.
In browser devtools,
panicked at 'time not implemented on this platform', library/std/src/sys/wasm/../unsupported/time.rs:13:9
Can replace time with gloo, but probably might not work on dioxus-desktop?
https://gloo-rs.web.app/
clone) due to lifetime let start = || {
if !*started.get() {
started.set(true); // this cause infinite loop
let count = count.clone(); // clone reference rather than value
let alert = move || gloo_dialogs::alert(&format!("Your score was {}!", count.read()));
gloo_timers::callback::Timeout::new(5_000, alert).forget();
}
// started.set(true); // this cannot be done inside condition or infinite loop
};