feat: mvoe away from compound context

This commit is contained in:
Jonathan Kelley committed 2021-09-21 13:42:52 -04:00
1 parent bfdcb20437
commit a2c7d17b05
103 files changed
+309 -293

No files matched your search

+21 -8
View File
@@ -78,15 +78,16 @@ Now, let's edit our `main.rs` file:
```rust
use diouxs::prelude::*;
fn main() {
dioxus::web::start(App)
dioxus::web::start(App, |c| c).unwrap();
}
fn App(cx: Context<()>) -> DomTree {
static App: FC<()> = |cx, props| {
cx.render(rsx! {
div { "Hello, world!" }
})
}
};
```
Let's dissect our example a bit.
@@ -97,24 +98,36 @@ This bit of code imports everything from the the `prelude` module. This brings i
use diouxs::prelude::*;
```
This bit of code starts the WASM renderer as a future (JS Promise) and then awaits it. This is very similar to the `ReactDOM.render()` method you use for a React app. We pass in the `App` function as a our app.
This bit of code starts the WASM renderer as a future (JS Promise) and then awaits it. This is very similar to the `ReactDOM.render()` method you use for a React app. We pass in the `App` function as a our app. The `|c| c` closure provides a way to configure the WebSys app, enabling things like hydration, pre-rendering, and other tweaks. The simplest configuration is just the defaults.
Calling "start" will launch Dioxus on the web's main thread, running `wasm_bindgen_futures::block_on`. It's possible to "launch" the WebSys renderer as an async function - check the documentation for more details.
```rust
fn main() {
dioxus::web::start(App)
dioxus::web::start(App, |c| c).unwrap();
}
```
Finally, our app. Every component in Dioxus is a function that takes in a `Context` object and returns a `VNode`.
Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Option<VNode>`.
```rust
fn App(cx: Context<()>) -> DomTree {
static App: FC<()> = |cx, props| {
cx.render(rsx! {
div { "Hello, world!" }
})
};
```
The closure `FC<()>` syntax is identical to the function syntax, but with lifetimes managed for you. In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax:
```rust
fn App<'a>(cx: Context<'a>, props: &()) -> DomTree<'a> {
cx.render(rsx! {
div { "Hello, world!" }
})
}
```
In React, you'll save data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering. In Dioxus, you are given an explicit `Context` object to control how the component renders and stores data.
Next, we're greeted with the `rsx!` macro. This lets us add a custom DSL for declaratively building the structure of our app. The semantics of this macro are similar to that of JSX and HTML, though with a familiar Rust-y interface. The `html!` macro is also available for writing components with a JSX/HTML syntax.
@@ -135,7 +148,7 @@ If we wanted to golf a bit, we can shrink our hello-world even smaller:
```rust
fn main() {
dioxus::web::start(|cx| cx.render(diouxs::rsx!( div { "Hello, World!" } ))
dioxus::web::start(|cx, _| cx.render(diouxs::rsx!( div { "Hello, World!" } ))
}
```
+10 -12
View File
@@ -12,15 +12,14 @@ You'll want to write RSX where you can, and in a future release we'll have a too
```rust
#[derive(PartialEq, Props)]
struct ExampleProps { name: &str, pending: bool, count: i32 }
struct ExampleProps { name: &'static str, pending: bool, count: i32 }
fn Example(cx: Context<ExampleProps> ) -> DomTree {
let ExampleProps { name, pending, count } = cx.props;
static Example: FC<ExampleProps> = |cx, props| {
cx.render(html! {
<div>
<p> "Hello, {name}!" </p>
<p> "Status: {pending}!" </p>
<p> "Count {count}!" </p>
<p> "Hello, {props.name}!" </p>
<p> "Status: {props.pending}!" </p>
<p> "Count {props.count}!" </p>
</div>
})
}
@@ -35,13 +34,12 @@ The Dioxus VSCode extension will eventually provide a macro to convert a selecti
It's also a bit easier on the eyes 🙂 than HTML.
```rust
fn Example(cx: Context<ExampleProps>) -> DomTree {
static Example: FC<ExampleProps> = |cx, props| {
cx.render(rsx! {
div {
// cx derefs to props so you can access fields directly
p {"Hello, {cx.name}!"}
p {"Status: {cx.pending}!"}
p {"Count {cx.count}!"}
p {"Hello, {props.name}!"}
p {"Status: {props.pending}!"}
p {"Count {props.count}!"}
}
})
}
@@ -60,7 +58,7 @@ Commas are entirely optional, but might be useful to delineate between elements
The `render` function provides an **extremely efficient** allocator for VNodes and text, so try not to use the `format!` macro in your components. Rust's default `ToString` methods pass through the global allocator, but all text in components is allocated inside a manually-managed Bump arena. To push you in the right direction, all text-based attributes take `std::fmt::Arguments` directly, so you'll want to reach for `format_args!` when the built-in `f-string` interpolation just doesn't cut it.
```rust
pub static Example: FC<()> = |cx| {
pub static Example: FC<()> = |cx, props|{
let text = "example";
+2 -2
View File
@@ -1,5 +1,5 @@
```rust
fn Example(cx: &mut Context<()>) -> DomTree {
fn Example<'a>(cx: Context<'a>, props: &()) -> DomTree<'a> {
let service = use_combubulator(cx);
let Status { name, pending, count } = service.info();
html! {
@@ -7,7 +7,7 @@ fn Example(cx: &mut Context<()>) -> DomTree {
<p> "Hello, {name}!" </p>
<p> "Status: {pending}!" </p>
<p> "Count {count}!" </p>
<button onclick=|_| service.update()>
<button onclick={|_| service.update()}>
"Refresh services"
</button>
</div>
+2 -2
View File
@@ -50,8 +50,8 @@ async fn ExampleLoader(cx: Context<()>) -> Vnode {
This API stores the result on the Context object, so the loaded data is taken as reference.
*/
let name: &Result<SomeStructure> = use_fetch_data("http://example.com/json", ())
.place_holder(|cx| rsx!{<div> "loading..." </div>})
.delayed_place_holder(1000, |cx| rsx!{ <div> "still loading..." </div>})
.place_holder(|cx, props|rsx!{<div> "loading..." </div>})
.delayed_place_holder(1000, |cx, props|rsx!{ <div> "still loading..." </div>})
.await;
match name {
+3 -3
View File
@@ -21,9 +21,9 @@ fn test() -> DomTree {
}
}
static TestComponent: FC<()> = |cx| html!{<div>"Hello world"</div>};
static TestComponent: FC<()> = |cx, props|html!{<div>"Hello world"</div>};
static TestComponent: FC<()> = |cx| {
static TestComponent: FC<()> = |cx, props|{
let g = "BLAH";
html! {
<div> "Hello world" </div>
@@ -31,7 +31,7 @@ static TestComponent: FC<()> = |cx| {
};
#[functional_component]
static TestComponent: FC<{ name: String }> = |cx| html! { <div> "Hello {name}" </div> };
static TestComponent: FC<{ name: String }> = |cx, props|html! { <div> "Hello {name}" </div> };
```
## Why this behavior?
+3 -3
View File
@@ -96,7 +96,7 @@ Sometimes you want a signal to propagate across your app, either through far-awa
```rust
const TITLE: Atom<String> = || "".to_string();
const Provider: FC<()> = |cx| {
const Provider: FC<()> = |cx, props|{
let title = use_signal(&cx, &TITLE);
rsx!(cx, input { value: title })
};
@@ -105,7 +105,7 @@ const Provider: FC<()> = |cx| {
If we use the `TITLE` atom in another component, we can cause updates to flow between components without calling render or diffing either component trees:
```rust
const Receiver: FC<()> = |cx| {
const Receiver: FC<()> = |cx, props|{
let title = use_signal(&cx, &TITLE);
log::info!("This will only be called once!");
rsx!(cx,
@@ -132,7 +132,7 @@ Dioxus automatically understands how to use your signals when mixed with iterato
```rust
const DICT: AtomFamily<String, String> = |_| {};
const List: FC<()> = |cx| {
const List: FC<()> = |cx, props|{
let dict = use_signal(&cx, &DICT);
cx.render(rsx!(
ul {