wip: some docs and suspense
This commit is contained in:
1 parent
bab21a0aa1
commit
93d4b8ca7c
22 files changed
+210
-252
No files matched your search
@@ -26,7 +26,7 @@ container.push(green_light);
|
||||
container.push(yellow_light);
|
||||
container.push(red_light);
|
||||
|
||||
container.onclick(move |_| {
|
||||
container.set_onclick(move |_| {
|
||||
if red_light.enabled() {
|
||||
red_light.set_enabled(false);
|
||||
green_light.set_enabled(true);
|
||||
@@ -45,22 +45,23 @@ As the UI grows in scale, our logic to keep each element in the proper state wou
|
||||
Instead, with Dioxus, we *declare* what we want our UI to look like:
|
||||
|
||||
```rust
|
||||
let state = "red";
|
||||
let mut state = use_state(cx, || "red");
|
||||
|
||||
rsx!(
|
||||
cx.render(rsx!(
|
||||
Container {
|
||||
Light { color: "red", enabled: {state == "red"} }
|
||||
Light { color: "yellow", enabled: {state == "yellow"} }
|
||||
Light { color: "green", enabled: {state == "green"} }
|
||||
onclick: |_| {
|
||||
state = match state {
|
||||
Light { color: "red", enabled: format_args!("{}", state == "red") }
|
||||
Light { color: "yellow", enabled: format_args!("{}", state == "yellow") }
|
||||
Light { color: "green", enabled: format_args!("{}", state == "green") }
|
||||
|
||||
onclick: move |_| {
|
||||
state.set(match *state {
|
||||
"green" => "yellow",
|
||||
"yellow" => "red",
|
||||
"red" => "green",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
))
|
||||
```
|
||||
|
||||
Remember: this concept is not new! Many frameworks are declarative - with React being the most popular. Declarative frameworks tend to be much more enjoyable to work with than imperative frameworks.
|
||||
|
||||
@@ -84,7 +84,7 @@ struct PostProps {
|
||||
|
||||
And our render function:
|
||||
```rust
|
||||
fn Post((cx, props): Scope<PostProps>) -> Element {
|
||||
fn Post(cx: Context, props: &PostProps) -> Element {
|
||||
cx.render(rsx!{
|
||||
div { class: "post-container"
|
||||
VoteButton {
|
||||
@@ -120,7 +120,7 @@ struct VoteButtonProps {
|
||||
score: i32
|
||||
}
|
||||
|
||||
fn VoteButton((cx, props): Scope<VoteButtonProps>) -> Element {
|
||||
fn VoteButton(cx: Context, props: &VoteButtonProps) -> Element {
|
||||
cx.render(rsx!{
|
||||
div { class: "votebutton"
|
||||
div { class: "arrow up" }
|
||||
@@ -145,7 +145,7 @@ struct TitleCardProps<'a> {
|
||||
title: &'a str,
|
||||
}
|
||||
|
||||
fn TitleCard<'a>((cx, props): Scope<'a, TitleCardProps>) -> Element<'a> {
|
||||
fn TitleCard(cx: Context, props: &TitleCardProps) -> Element {
|
||||
cx.render(rsx!{
|
||||
h1 { "{props.title}" }
|
||||
})
|
||||
@@ -174,8 +174,8 @@ function Component(props) {
|
||||
Because Dioxus needs to work with the rules of Rust it uses the `Context` object to maintain some internal bookkeeping. That's what the `Context` object is: a place for the component to store state, manage listeners, and allocate elements. Advanced users of Dioxus will want to learn how to properly leverage the `Context` object to build robust and performant extensions for Dioxus.
|
||||
|
||||
```rust
|
||||
fn Post((cx /* <-- our Context object*/, props): Scope<PostProps>) -> Element {
|
||||
cx.render(rsx!{ })
|
||||
fn Post(cx: Context, props: &PostProps) -> Element {
|
||||
cx.render(rsx!("hello"))
|
||||
}
|
||||
```
|
||||
## Moving forward
|
||||
|
||||
@@ -26,7 +26,7 @@ struct AppProps {
|
||||
Now that we have a "logged_in" flag accessible in our props, we can render two different screens:
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Scope<AppProps>) -> Element {
|
||||
fn App(cx: Context, props: &AppProps) -> Element {
|
||||
if props.logged_in {
|
||||
cx.render(rsx!{
|
||||
DashboardScreen {}
|
||||
@@ -48,7 +48,7 @@ Rust provides us algebraic datatypes: enums that can contain values. Using the `
|
||||
For instance, we could run a function that returns a Result:
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
fn App(cx: Context, props: &())-> Element {
|
||||
match get_name() {
|
||||
Ok(name) => cx.render(rsx!( "Hello, {name}!" )),
|
||||
Err(err) => cx.render(rsx!( "Sorry, I don't know your name, because an error occurred: {err}" )),
|
||||
@@ -58,7 +58,7 @@ fn App((cx, props): Scope<()>) -> Element {
|
||||
|
||||
We can even match against values:
|
||||
```rust
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
fn App(cx: Context, props: &())-> Element {
|
||||
match get_name() {
|
||||
"jack" => cx.render(rsx!( "Hey Jack, how's Diane?" )),
|
||||
"diane" => cx.render(rsx!( "Hey Diana, how's Jack?" )),
|
||||
@@ -67,12 +67,12 @@ fn App((cx, props): Scope<()>) -> Element {
|
||||
}
|
||||
```
|
||||
|
||||
Do note: the `rsx!` macro returns a `Closure`, an anonymous function that has a unique type.
|
||||
Do note: the `rsx!` macro returns a `Closure`, an anonymous function that has a unique type. To turn our `rsx!` into Elements, we need to call `cx.render`.
|
||||
|
||||
To make patterns like these less verbose, the `rsx!` macro accepts an optional first argument on which it will call `render`. Our previous component can be shortened with this alternative syntax:
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
fn App(cx: Context, props: &())-> Element {
|
||||
match get_name() {
|
||||
"jack" => rsx!(cx, "Hey Jack, how's Diane?" ),
|
||||
"diane" => rsx!(cx, "Hey Diana, how's Jack?" ),
|
||||
@@ -83,13 +83,13 @@ fn App((cx, props): Scope<()>) -> Element {
|
||||
|
||||
This syntax even enables us to write a one-line component:
|
||||
```rust
|
||||
static App: Fc<()> = |cx, props| rsx!(cx, "hello world!");
|
||||
static App: FC<()> = |cx, props| rsx!(cx, "hello world!");
|
||||
```
|
||||
|
||||
Alternatively, for match statements, we can just return the builder itself and pass it into a final, single call to `cx.render`:
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
fn App(cx: Context, props: &())-> Element {
|
||||
let greeting = match get_name() {
|
||||
"jack" => rsx!("Hey Jack, how's Diane?" ),
|
||||
"diane" => rsx!("Hey Diana, how's Jack?" ),
|
||||
|
||||
@@ -35,7 +35,7 @@ fn main() {
|
||||
When Dioxus renders your app, it will pass an immutable reference of `PostProps` to your `Post` component. Here, you can pass the state down into children.
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Scope<PostProps>) -> Element {
|
||||
fn App(cx: Context, props: &PostProps) -> Element {
|
||||
cx.render(rsx!{
|
||||
Title { title: &props.title }
|
||||
Score { score: &props.score }
|
||||
@@ -63,8 +63,8 @@ Instead, you'll want to store state internally in your components and let *that*
|
||||
The most common hook you'll use for storing state is `use_state`. `use_state` provides a slot for some data that allows you to read and update the value without accidentally mutating it.
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
let post = use_state(|| {
|
||||
fn App(cx: Context, props: &())-> Element {
|
||||
let post = use_state(cx, || {
|
||||
PostData {
|
||||
id: Uuid::new_v4(),
|
||||
score: 10,
|
||||
@@ -111,8 +111,8 @@ When responding to user-triggered events, we'll want to "listen" for an event on
|
||||
For example, let's say we provide a button to generate a new post. Whenever the user clicks the button, they get a new post. To achieve this functionality, we'll want to attach a function to the `on_click` method of `button`. Whenever the button is clicked, our function will run, and we'll get new Post data to work with.
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
let post = use_state(|| PostData::new());
|
||||
fn App(cx: Context, props: &())-> Element {
|
||||
let post = use_state(cx, || PostData::new());
|
||||
|
||||
cx.render(rsx!{
|
||||
button {
|
||||
@@ -134,8 +134,8 @@ We can use tasks in our components to build a tiny stopwatch that ticks every se
|
||||
|
||||
```rust
|
||||
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
let mut sec_elapsed = use_state(|| 0);
|
||||
fn App(cx: Context, props: &())-> Element {
|
||||
let mut sec_elapsed = use_state(cx, || 0);
|
||||
|
||||
cx.spawn_task(async move {
|
||||
TimeoutFuture::from_ms(1000).await;
|
||||
|
||||
@@ -74,7 +74,7 @@ struct PostListProps<'a> {
|
||||
Next, we're going to define our component:
|
||||
|
||||
```rust
|
||||
fn App((cx, props): Component<PostList>) -> Element {
|
||||
fn App(cx: Context, props: &PostList) -> Element {
|
||||
// First, we create a new iterator by mapping the post array
|
||||
let posts = props.posts.iter().map(|post| rsx!{
|
||||
Post {
|
||||
|
||||
@@ -1 +1,54 @@
|
||||
# Suspense
|
||||
|
||||
Suspense in Dioxus is enabled through placeholder nodes.
|
||||
|
||||
just a component that renders nodes into the placeholder after the future is finished?
|
||||
|
||||
in react, suspense is just completely pausing diffing while a
|
||||
|
||||
```rust
|
||||
let n = use_suspense(cx || {
|
||||
cx.render(rsx!{
|
||||
Suspense {
|
||||
prom: fut,
|
||||
callback: || {}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
suspense () {
|
||||
let value = use_state();
|
||||
if first_render {
|
||||
push_task({
|
||||
value.set(fut.await);
|
||||
});
|
||||
} else {
|
||||
callback(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let name = fetch_name().await;
|
||||
|
||||
|
||||
function ProfileDetails() {
|
||||
// Try to read user info, although it might not have loaded yet
|
||||
const user = resource.read();
|
||||
return <h1>{user.name}</h1>;
|
||||
}
|
||||
|
||||
|
||||
fn ProfileDteails() {
|
||||
let user = resource.suspend(cx, |l| rsx!("{l}"));
|
||||
|
||||
// waits for the resource to be ready and updates the placeholder with the tree
|
||||
let name = resource.suspend_with(cx, |val| rsx!( div { "hello" "{user}" } ));
|
||||
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
{user}
|
||||
{name}
|
||||
}
|
||||
))
|
||||
}
|
||||
```
|
||||
@@ -107,7 +107,7 @@ To do this, we use the familiar struct-style syntax that Rust provides. Commas a
|
||||
```rust
|
||||
rsx!(
|
||||
div {
|
||||
hidden: true,
|
||||
hidden: "true",
|
||||
background_color: "blue",
|
||||
class: "card color-{mycolor}"
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user