Browse Source

docs: move into a fromjs tutorial

Jonathan Kelley 4 năm trước cách đây
mục cha
commit
69f5cc3802

+ 5 - 45
README.md

@@ -9,13 +9,13 @@ Dioxus is a portable, performant, and ergonomic framework for building cross-pla
 
 ```rust
 fn Example(ctx: Context<()>) -> VNode {
-    let selection = use_state(ctx, move || "..?");
+    let (selection, set_selection) = use_state(&ctx, move || "..?");
 
     ctx.render(rsx! {
         div {
             h1 { "Hello, {selection}" }
-            button { "?", onclick: move |_| selection.set("world!")}
-            button { "?", onclick: move |_| selection.set("Dioxus 🎉")}
+            button { "?", onclick: move |_| set_selection("world!")}
+            button { "?", onclick: move |_| set_selection("Dioxus 🎉")}
         }
     })
 };
@@ -70,9 +70,7 @@ If you know React, then you already know Dioxus.
 - [Tailwind for Dioxus]()
 - [The monoglot startup]()
 
-## FAQ
-
-### Why?
+## Why?
 
 ---
 
@@ -88,44 +86,6 @@ TypeScript is a great addition to JavaScript, but comes with a lot of tweaking f
 - best-in-class error handling
 - simple and fast build system
 - include_str! for integrating html/css/svg templates directly
-- various macros (html!, rsx!) for fast template iteration
+- various macros (`html!`, `rsx!`) for fast template iteration
 
 And much more. Dioxus makes Rust apps just as fast to write as React apps, but affords more robustness, giving your frontend team greater confidence in making big changes in shorter time. Dioxus also works on the server, on the web, on mobile, on desktop - and it runs completely natively so performance is never an issue.
-
-### Immutability by default?
-
----
-
-Rust, like JS and TS, supports both mutable and immutable data. With JS, `const` would be used to signify immutable data, while in rust, the absence of `mut` signifies immutable data.
-
-Mutability:
-
-```rust
-let mut val = 10; // rust
-let val = 10;     // js
-```
-
-Immutability
-
-```rust
-let val = 10;    // rust
-const val = 10;  // js
-```
-
-However, `const` in JS does not prohibit you from modify the value itself only disallowing assignment. In Rust, immutable **is immutable**. You _never_ have to work about accidentally mutating data; mutating immutable data in Rust requires deliberate advanced datastructures that you won't find in your typical frontend code.
-
-## How do strings work?
-
----
-
-In rust, we have `&str`, `&'static str` `String`, and `Rc<str>`. It's a lot, yes, and it might be confusing at first. But it's actually not too bad.
-
-In Rust, UTF-8 is supported natively, allowing for emoji and extended character sets (like Chinese and Arabic!) instead of the typical ASCII. The primitive `str` can be seen as a couple of UTF-8 code points squished together with a dynamic size. Because this size is variable (not known at compile time for any single character), we reference an array of UTF-8 code points as `&str`. Essentially, we're referencing (the & symbol) some dynamic `str` (a collection of UTF-8 points).
-
-For text encoded directly in your code, this collection of UTF-8 code points is given the `'static` reference lifetime - essentially meaning the text can be safely referenced for the entire runtime of your program. Contrast this with JS, where a string will only exist for as long as code references it before it gets cleaned up by the garbage collector.
-
-For text that needs to have characters added, removed, sorted, uppercased, formatted, accessed for mutation, etc, Rust has the `String` type, which is essentially just a dynamically sized `str`. In JS, if you add a character to your string, you actually create an entirely new string (completely cloning the old one first). In Rust, you can safely added characters to strings _without_ having to clone first, making string manipulation in Rust very efficient.
-
-Finally, we have `Rc<str>`. This is essentially Rust's version of JavaScript's `string`. In JS, whenever you pass a `string` around (and don't mutate it), you don't actually clone it, but rather just increment a counter that says "this code is using this string." This counter prevents the garbage collector from deleting the string before your code is done using it. Only when all parts of your code are done with the string, will the string be deleted. `Rc<str>` works exactly the same way in Rust, but requires a deliberate `.clone()` to get the same behavior. In most instances, Dioxus will automatically do this for you, saving the trouble of having to `clone` when you pass an `Rc<str>` into child components. `Rc<str>` is typically better than `String` for Rust - it allows cheap sharing of strings, and through `make_mut` you can always produce your own mutable copy for modifying. You might not see `Rc<str>` in other Rust libraries as much, but you will see it in Dioxus due to Dioxus' aggressive memoization and focus on efficiency and performance.
-
-If you run into issues with `&str`, `String`, `Rc<str>`, just try cloning and `to_string` first. For the vast majority of apps, the slight performance hit will be unnoticeable. Once you get better with Strings, it's very easy to go back and remove all the clones for more efficient alternatives, but you will likely never need to.

+ 37 - 0
docs/gettingstarted/fromjs.md

@@ -0,0 +1,37 @@
+### Immutability by default?
+
+---
+
+Rust, like JS and TS, supports both mutable and immutable data. With JS, `const` would be used to signify immutable data, while in rust, the absence of `mut` signifies immutable data.
+
+Mutability:
+
+```rust
+let mut val = 10; // rust
+let val = 10;     // js
+```
+
+Immutability
+
+```rust
+let val = 10;    // rust
+const val = 10;  // js
+```
+
+However, `const` in JS does not prohibit you from modify the value itself only disallowing assignment. In Rust, immutable **is immutable**. You _never_ have to work about accidentally mutating data; mutating immutable data in Rust requires deliberate advanced datastructures that you won't find in your typical frontend code.
+
+## How do strings work?
+
+---
+
+In rust, we have `&str`, `&'static str` `String`, and `Rc<str>`. It's a lot, yes, and it might be confusing at first. But it's actually not too bad.
+
+In Rust, UTF-8 is supported natively, allowing for emoji and extended character sets (like Chinese and Arabic!) instead of the typical ASCII. The primitive `str` can be seen as a couple of UTF-8 code points squished together with a dynamic size. Because this size is variable (not known at compile time for any single character), we reference an array of UTF-8 code points as `&str`. Essentially, we're referencing (the & symbol) some dynamic `str` (a collection of UTF-8 points).
+
+For text encoded directly in your code, this collection of UTF-8 code points is given the `'static` reference lifetime - essentially meaning the text can be safely referenced for the entire runtime of your program. Contrast this with JS, where a string will only exist for as long as code references it before it gets cleaned up by the garbage collector.
+
+For text that needs to have characters added, removed, sorted, uppercased, formatted, accessed for mutation, etc, Rust has the `String` type, which is essentially just a dynamically sized `str`. In JS, if you add a character to your string, you actually create an entirely new string (completely cloning the old one first). In Rust, you can safely added characters to strings _without_ having to clone first, making string manipulation in Rust very efficient.
+
+Finally, we have `Rc<str>`. This is essentially Rust's version of JavaScript's `string`. In JS, whenever you pass a `string` around (and don't mutate it), you don't actually clone it, but rather just increment a counter that says "this code is using this string." This counter prevents the garbage collector from deleting the string before your code is done using it. Only when all parts of your code are done with the string, will the string be deleted. `Rc<str>` works exactly the same way in Rust, but requires a deliberate `.clone()` to get the same behavior. In most instances, Dioxus will automatically do this for you, saving the trouble of having to `clone` when you pass an `Rc<str>` into child components. `Rc<str>` is typically better than `String` for Rust - it allows cheap sharing of strings, and through `make_mut` you can always produce your own mutable copy for modifying. You might not see `Rc<str>` in other Rust libraries as much, but you will see it in Dioxus due to Dioxus' aggressive memoization and focus on efficiency and performance.
+
+If you run into issues with `&str`, `String`, `Rc<str>`, just try cloning and `to_string` first. For the vast majority of apps, the slight performance hit will be unnoticeable. Once you get better with Strings, it's very easy to go back and remove all the clones for more efficient alternatives, but you will likely never need to.

+ 1 - 1
examples/webview.rs

@@ -11,7 +11,7 @@ use dioxus::prelude::*;
 
 async fn main() {
     dioxus_webview::launch(|ctx| {
-        let (count, set_count) = use_state(ctx, || 0);
+        let (count, set_count) = use_state(&ctx, || 0);
 
         ctx.render(rsx! {
             div {

+ 1 - 1
packages/core/examples/nested.rs

@@ -8,7 +8,7 @@ use dioxus_core::prelude::*;
 fn main() {}
 
 static Header: FC<()> = |ctx| {
-    let inner = use_ref(ctx, || 0);
+    let inner = use_ref(&ctx, || 0);
 
     let handler1 = move || println!("Value is {}", inner.borrow());
 

+ 11 - 11
packages/core/src/hooks.rs

@@ -29,7 +29,7 @@ mod use_state_def {
     /// Usage:
     /// ```ignore
     /// static Example: FC<()> = |ctx| {
-    ///     let (counter, set_counter) = use_state(ctx, || 0);
+    ///     let (counter, set_counter) = use_state(&ctx, || 0);
     ///     let increment = |_| set_couter(counter + 1);
     ///     let decrement = |_| set_couter(counter + 1);
     ///
@@ -42,8 +42,8 @@ mod use_state_def {
     ///     }
     /// }
     /// ```
-    pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P: 'a>(
-        ctx: Context<'a, P>,
+    pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>(
+        ctx: &impl Scoped<'a>,
         initial_state_fn: F,
     ) -> (&'a T, &'a Rc<dyn Fn(T)>) {
         struct UseState<T: 'static> {
@@ -156,7 +156,7 @@ mod new_use_state_def {
     /// Usage:
     /// ```ignore
     /// static Example: FC<()> = |ctx| {
-    ///     let (counter, set_counter) = use_state(ctx, || 0);
+    ///     let (counter, set_counter) = use_state(&ctx, || 0);
     ///     let increment = |_| set_couter(counter + 1);
     ///     let decrement = |_| set_couter(counter + 1);
     ///
@@ -169,8 +169,8 @@ mod new_use_state_def {
     ///     }
     /// }
     /// ```
-    pub fn use_state_new<'a, 'c, T: 'static, F: FnOnce() -> T, P: 'static>(
-        ctx: Context<'a, P>,
+    pub fn use_state_new<'a, 'c, T: 'static, F: FnOnce() -> T>(
+        ctx: &impl Scoped<'a>,
         initial_state_fn: F,
     ) -> &'a UseState<T> {
         ctx.use_hook(
@@ -246,8 +246,8 @@ mod use_ref_def {
     /// To change it, use modify.
     /// Modifications to this value do not cause updates to the component
     /// Attach to inner context reference, so context can be consumed
-    pub fn use_ref<'a, T: 'static, P>(
-        ctx: Context<'a, P>,
+    pub fn use_ref<'a, T: 'static>(
+        ctx: &impl Scoped<'a>,
         initial_state_fn: impl FnOnce() -> T + 'static,
     ) -> &'a RefCell<T> {
         ctx.use_hook(|| RefCell::new(initial_state_fn()), |state| &*state, |_| {})
@@ -270,8 +270,8 @@ mod use_reducer_def {
     ///
     /// This is behaves almost exactly the same way as React's "use_state".
     ///
-    pub fn use_reducer<'a, 'c, State: 'static, Action: 'static, P: 'static>(
-        ctx: Context<'a, P>,
+    pub fn use_reducer<'a, 'c, State: 'static, Action: 'static>(
+        ctx: &impl Scoped<'a>,
         initial_state_fn: impl FnOnce() -> State,
         _reducer: impl Fn(&mut State, Action),
     ) -> (&'a State, &'a Box<dyn Fn(Action)>) {
@@ -348,7 +348,7 @@ mod use_reducer_def {
 }
 
 pub fn use_is_initialized<P>(ctx: Context<P>) -> bool {
-    let val = use_ref(ctx, || false);
+    let val = use_ref(&ctx, || false);
     match *val.borrow() {
         true => true,
         false => {

+ 1 - 0
packages/core/src/lib.rs

@@ -115,6 +115,7 @@ pub mod prelude {
     pub use crate::component::{fc_to_builder, Properties};
     use crate::nodes;
     pub use crate::virtual_dom::Context;
+    pub use crate::virtual_dom::Scoped;
     pub use nodes::*;
 
     pub use crate::nodebuilder::LazyNodes;

+ 30 - 49
packages/core/src/virtual_dom.rs

@@ -526,6 +526,9 @@ pub struct Scope {
     pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
 }
 
+// We need to pin the hook so it doesn't move as we initialize the list of hooks
+type Hook = Pin<Box<dyn std::any::Any>>;
+
 impl Scope {
     // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
     // we are going to break this lifetime by force in order to save it on ourselves.
@@ -718,36 +721,27 @@ impl<'a, T> Deref for Context<'a, T> {
     }
 }
 
-impl<'src, T> Context<'src, T> {
+impl<'src, T> Scoped<'src> for Context<'src, T> {
+    fn get_scope(&self) -> &'src Scope {
+        self.scope
+    }
+}
+
+pub trait Scoped<'src>: Sized {
+    fn get_scope(&self) -> &'src Scope;
+
     /// Access the children elements passed into the component
-    pub fn children(&self) -> &'src [VNode<'src>] {
+    fn children(&self) -> &'src [VNode<'src>] {
         todo!("Children API not yet implemented for component Context")
     }
 
     /// Create a subscription that schedules a future render for the reference component
-    pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
+    fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
         todo!()
         // pub fn schedule_update(self) -> impl Fn() + 'static {
         // self.scope.event_queue.schedule_update(&self.scope)
     }
 
-    // /// Create a suspended component from a future.
-    // ///
-    // /// When the future completes, the component will be renderered
-    // pub fn suspend<'a, F: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a>(
-    //     &'a self,
-    //     _fut: impl Future<Output = LazyNodes<'a, F>>,
-    // ) -> VNode<'src> {
-    //     todo!()
-    // }
-}
-
-// ================================================
-//       Render Implementation for Components
-// ================================================
-//
-impl<'src, T> Context<'src, T> {
-    // impl<'scope> Context<'scope> {
     /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
     ///
     /// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
@@ -763,14 +757,15 @@ impl<'src, T> Context<'src, T> {
     ///     ctx.render(lazy_tree)
     /// }
     ///```
-    pub fn render<'a, F: for<'b> FnOnce(&'b NodeCtx<'src>) -> VNode<'src> + 'src + 'a>(
+    fn render<'a, F: for<'b> FnOnce(&'b NodeCtx<'src>) -> VNode<'src> + 'src + 'a>(
         self,
         // &'a/ self,
         lazy_nodes: LazyNodes<'src, F>,
         // lazy_nodes: LazyNodes<'src, F>,
     ) -> VNode<'src> {
+        let scope_ref = self.get_scope();
         let ctx = NodeCtx {
-            scope_ref: self.scope,
+            scope_ref,
             listener_id: 0.into(),
         };
 
@@ -781,16 +776,7 @@ impl<'src, T> Context<'src, T> {
         //     },
         // }
     }
-}
-
-// ================================================
-//       Hooks Implementation for Components
-// ================================================
-
-// We need to pin the hook so it doesn't move as we initialize the list of hooks
-type Hook = Pin<Box<dyn std::any::Any>>;
 
-impl<'src, T> Context<'src, T> {
     // impl<'scope> Context<'scope> {
     /// Store a value between renders
     ///
@@ -808,7 +794,7 @@ impl<'src, T> Context<'src, T> {
     ///     )
     /// }
     /// ```
-    pub fn use_hook<InternalHookState: 'static, Output: 'src>(
+    fn use_hook<InternalHookState: 'static, Output: 'src>(
         &self,
 
         // The closure that builds the hook state
@@ -821,10 +807,12 @@ impl<'src, T> Context<'src, T> {
         // TODO: add this to the "clean up" group for when the component is dropped
         _cleanup: impl FnOnce(InternalHookState),
     ) -> Output {
-        let idx = *self.scope.hookidx.borrow();
+        let scope = self.get_scope();
+
+        let idx = *scope.hookidx.borrow();
 
         // Grab out the hook list
-        let mut hooks = self.scope.hooks.borrow_mut();
+        let mut hooks = scope.hooks.borrow_mut();
 
         // If the idx is the same as the hook length, then we need to add the current hook
         if idx >= hooks.len() {
@@ -832,7 +820,7 @@ impl<'src, T> Context<'src, T> {
             hooks.push(Box::pin(new_state));
         }
 
-        *self.scope.hookidx.borrow_mut() += 1;
+        *scope.hookidx.borrow_mut() += 1;
 
         let stable_ref = hooks
             .get_mut(idx)
@@ -854,12 +842,7 @@ Any function prefixed with "use" should not be called conditionally.
         // We extend the lifetime of the internal state
         runner(unsafe { &mut *(internal_state as *mut _) })
     }
-}
 
-// ================================================
-//   Context API Implementation for Components
-// ================================================
-impl<'src, P> Context<'src, P> {
     /// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
     ///
     /// This is a hook, so it may not be called conditionally!
@@ -872,8 +855,9 @@ impl<'src, P> Context<'src, P> {
     ///
     ///
     ///
-    pub fn use_create_context<T: 'static>(&self, init: impl Fn() -> T) {
-        let mut ctxs = self.scope.shared_contexts.borrow_mut();
+    fn use_create_context<T: 'static>(&self, init: impl Fn() -> T) {
+        let scope = self.get_scope();
+        let mut ctxs = scope.shared_contexts.borrow_mut();
         let ty = TypeId::of::<T>();
 
         let is_initialized = self.use_hook(
@@ -901,12 +885,12 @@ impl<'src, P> Context<'src, P> {
     }
 
     /// There are hooks going on here!
-    pub fn use_context<T: 'static>(&self) -> &'src Rc<T> {
+    fn use_context<T: 'static>(&self) -> &'src Rc<T> {
         self.try_use_context().unwrap()
     }
 
     /// Uses a context, storing the cached value around
-    pub fn try_use_context<T: 'static>(&self) -> Result<&'src Rc<T>> {
+    fn try_use_context<T: 'static>(&self) -> Result<&'src Rc<T>> {
         struct UseContextHook<C> {
             par: Option<Rc<C>>,
             we: Option<Weak<C>>,
@@ -918,7 +902,8 @@ impl<'src, P> Context<'src, P> {
                 we: None as Option<Weak<T>>,
             },
             move |hook| {
-                let mut scope = Some(self.scope);
+                let scope = self.get_scope();
+                let mut scope = Some(scope);
 
                 if let Some(we) = &hook.we {
                     if let Some(re) = we.upgrade() {
@@ -964,10 +949,6 @@ impl<'src, P> Context<'src, P> {
     }
 }
 
-pub trait OpaqueScope {
-    fn get_scope(&self) -> &Scope;
-}
-
 // ==================================================================================
 //                Supporting structs for the above abstractions
 // ==================================================================================

+ 1 - 1
packages/docsite/src/snippets.md

@@ -34,7 +34,7 @@ enum LightState {
     Red,
 }
 static HelloMessage: FC<()> = |ctx| {
-    let (color, set_color) = use_state(ctx, || LightState::Green);
+    let (color, set_color) = use_state(&ctx, || LightState::Green);
 
     let title = match color {
         Green => "Green means go",

+ 1 - 1
packages/hooks/src/lib.rs

@@ -20,7 +20,7 @@ This is moderately efficient because the fields of the map are moved, but the da
 However, if you used similar approach with Dioxus:
 
 ```rust
-let (map, set_map) = use_state(ctx, || HashMap::new());
+let (map, set_map) = use_state(&ctx, || HashMap::new());
 set_map({
     let mut newmap = map.clone();
     newmap.set(key, value);

+ 4 - 4
packages/webview/README.md

@@ -1,6 +1,6 @@
 # Dioxus-webview
 
-Dioxus-webview bridges virtual and Webview DOMs together to make simple, portable, desktop applications. 
+Dioxus-webview bridges virtual and Webview DOMs together to make simple, portable, desktop applications.
 
 Dioxus-webview is an attempt at making a simpler "Tauri" where creating desktop applications is as simple as:
 
@@ -9,7 +9,7 @@ Dioxus-webview is an attempt at making a simpler "Tauri" where creating desktop
 #[async_std::main]
 async fn main() {
    dioxus_webview::new(|ctx| {
-       let (count, set_count) = use_state(ctx, || 0);
+       let (count, set_count) = use_state(&ctx, || 0);
        html! {
             <div>
                 <h1> "Dioxus Desktop Demo" </h1>
@@ -21,7 +21,7 @@ async fn main() {
        }
    })
    .configure_webview(|view| {
-      // custom webview config options 
+      // custom webview config options
    })
    .launch()
    .await;
@@ -44,4 +44,4 @@ By bridging the native process, desktop apps can access full multithreading powe
 
 Dioxus-desktop is a pure liveview application where all of the state and event handlers are proxied through the liveview and into the native process. For pure server-based liveview, this would normally be too slow (in both render performance and latency), but because the VDom is local, desktop apps are just as fast as Electron.
 
-Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime *is* needed - we recommend async-std in tokio mode.
+Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime _is_ needed - we recommend async-std in tokio mode.