From 9731640434cb8f7b5079d0ac1862262e19c2ed6b Mon Sep 17 00:00:00 2001 From: DianQK Date: Wed, 21 Jun 2023 20:07:49 +0800 Subject: [PATCH 01/20] Revert "fix: use typeid of atoms for their unique ID (#558)" This reverts commit bf9901a6cd98d9487174f00b9b344f6105677017. --- packages/fermi/src/atoms/atom.rs | 9 +++------ packages/fermi/src/atoms/atomfamily.rs | 9 +++------ packages/fermi/src/atoms/atomref.rs | 7 ++----- packages/fermi/src/root.rs | 24 +++--------------------- 4 files changed, 11 insertions(+), 38 deletions(-) diff --git a/packages/fermi/src/atoms/atom.rs b/packages/fermi/src/atoms/atom.rs index 81e04ce0b..2b99174b5 100644 --- a/packages/fermi/src/atoms/atom.rs +++ b/packages/fermi/src/atoms/atom.rs @@ -3,7 +3,7 @@ use crate::{AtomId, AtomRoot, Readable, Writable}; pub type Atom = fn(AtomBuilder) -> T; pub struct AtomBuilder; -impl Readable for Atom { +impl Readable for Atom { fn read(&self, _root: AtomRoot) -> Option { todo!() } @@ -11,14 +11,11 @@ impl Readable for Atom { (*self)(AtomBuilder) } fn unique_id(&self) -> AtomId { - AtomId { - ptr: *self as *const (), - type_id: std::any::TypeId::of::(), - } + *self as *const () } } -impl Writable for Atom { +impl Writable for Atom { fn write(&self, _root: AtomRoot, _value: V) { todo!() } diff --git a/packages/fermi/src/atoms/atomfamily.rs b/packages/fermi/src/atoms/atomfamily.rs index 29cecd4b2..60fc22bcb 100644 --- a/packages/fermi/src/atoms/atomfamily.rs +++ b/packages/fermi/src/atoms/atomfamily.rs @@ -4,7 +4,7 @@ use im_rc::HashMap as ImMap; pub struct AtomFamilyBuilder; pub type AtomFamily = fn(AtomFamilyBuilder) -> ImMap; -impl Readable> for AtomFamily { +impl Readable> for AtomFamily { fn read(&self, _root: AtomRoot) -> Option> { todo!() } @@ -14,14 +14,11 @@ impl Readable> for AtomFamily { } fn unique_id(&self) -> AtomId { - AtomId { - ptr: *self as *const (), - type_id: std::any::TypeId::of::(), - } + *self as *const () } } -impl Writable> for AtomFamily { +impl Writable> for AtomFamily { fn write(&self, _root: AtomRoot, _value: ImMap) { todo!() } diff --git a/packages/fermi/src/atoms/atomref.rs b/packages/fermi/src/atoms/atomref.rs index c103fa662..59e412031 100644 --- a/packages/fermi/src/atoms/atomref.rs +++ b/packages/fermi/src/atoms/atomref.rs @@ -4,7 +4,7 @@ use std::cell::RefCell; pub struct AtomRefBuilder; pub type AtomRef = fn(AtomRefBuilder) -> T; -impl Readable> for AtomRef { +impl Readable> for AtomRef { fn read(&self, _root: AtomRoot) -> Option> { todo!() } @@ -14,10 +14,7 @@ impl Readable> for AtomRef { } fn unique_id(&self) -> AtomId { - AtomId { - ptr: *self as *const (), - type_id: std::any::TypeId::of::(), - } + *self as *const () } } diff --git a/packages/fermi/src/root.rs b/packages/fermi/src/root.rs index 99e97bfbb..f77930c75 100644 --- a/packages/fermi/src/root.rs +++ b/packages/fermi/src/root.rs @@ -1,21 +1,11 @@ -use std::{ - any::{Any, TypeId}, - cell::RefCell, - collections::HashMap, - rc::Rc, - sync::Arc, -}; +use std::{any::Any, cell::RefCell, collections::HashMap, rc::Rc, sync::Arc}; use dioxus_core::ScopeId; use im_rc::HashSet; use crate::Readable; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct AtomId { - pub ptr: *const (), - pub type_id: TypeId, -} +pub type AtomId = *const (); pub struct AtomRoot { pub atoms: RefCell>, @@ -54,15 +44,7 @@ impl AtomRoot { // initialize the value if it's not already initialized if let Some(slot) = atoms.get_mut(&f.unique_id()) { slot.subscribers.insert(scope); - match slot.value.clone().downcast() { - Ok(res) => res, - Err(e) => panic!( - "Downcasting atom failed: {:?}. Has typeid of {:?} but needs typeid of {:?}", - f.unique_id(), - e.type_id(), - TypeId::of::() - ), - } + slot.value.clone().downcast().unwrap() } else { let value = Rc::new(f.init()); let mut subscribers = HashSet::new(); From 535435a4cf7ad8c3a1e6cc9bdb82eb4f705d544e Mon Sep 17 00:00:00 2001 From: DianQK Date: Wed, 21 Jun 2023 20:09:11 +0800 Subject: [PATCH 02/20] Fix the issue of duplicate unique ID for atoms using `newtype`. The MergeFunctionsPass in LLVM merges identical functions (https://rust.godbolt.org/z/3nnr9nMne), resulting in the same function addresses. --- .github/workflows/macos.yml | 4 ++- .github/workflows/windows.yml | 1 + .../en/__unused/advanced-guides/12-signals.md | 4 +-- docs/guide/src/en/async/use_coroutine.md | 8 ++--- .../src/en/best_practices/error_handling.md | 6 ++-- docs/guide/src/pt-br/async/use_coroutine.md | 8 ++--- .../pt-br/best_practices/error_handling.md | 6 ++-- examples/fermi.rs | 10 +++---- packages/fermi/README.md | 20 ++++++------- packages/fermi/src/atoms/atom.rs | 30 ++++++++++++++----- packages/fermi/src/atoms/atomfamily.rs | 10 +++---- packages/fermi/src/atoms/atomref.rs | 12 ++++---- packages/fermi/src/hooks/atom_ref.rs | 5 +++- packages/fermi/src/hooks/state.rs | 2 +- 14 files changed, 74 insertions(+), 52 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b80a7b1f5..eef04d521 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -33,4 +33,6 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - uses: actions/checkout@v3 - - run: cargo test --all --tests + - run: | + cargo test --all --tests + cargo test --package fermi --release diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0dd8a1842..368df1630 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -85,4 +85,5 @@ jobs: set RUST_BACKTRACE=1 cargo build --all --tests --examples cargo test --all --tests + cargo test --package fermi --release shell: cmd diff --git a/docs/guide/src/en/__unused/advanced-guides/12-signals.md b/docs/guide/src/en/__unused/advanced-guides/12-signals.md index c7012a119..486758c8d 100644 --- a/docs/guide/src/en/__unused/advanced-guides/12-signals.md +++ b/docs/guide/src/en/__unused/advanced-guides/12-signals.md @@ -95,7 +95,7 @@ Calling `deref` or `deref_mut` is actually more complex than it seems. When a va Sometimes you want a signal to propagate across your app, either through far-away siblings or through deeply-nested components. In these cases, we use Dirac: Dioxus's first-class state management toolkit. Dirac atoms automatically implement the Signal API. This component will bind the input element to the `TITLE` atom. ```rust -const TITLE: Atom = || "".to_string(); +const TITLE: Atom = Atom(|| "".to_string()); const Provider: Component = |cx|{ let title = use_signal(cx, &TITLE); render!(input { value: title }) @@ -131,7 +131,7 @@ By default, Dioxus is limited when you use iter/map. With the `For` component, y Dioxus automatically understands how to use your signals when mixed with iterators through `Deref`/`DerefMut`. This lets you efficiently map collections while avoiding the re-rendering of lists. In essence, signals act as a hint to Dioxus on how to avoid un-necessary checks and renders, making your app faster. ```rust -const DICT: AtomFamily = |_| {}; +const DICT: AtomFamily = AtomFamily(|_| {}); const List: Component = |cx|{ let dict = use_signal(cx, &DICT); cx.render(rsx!( diff --git a/docs/guide/src/en/async/use_coroutine.md b/docs/guide/src/en/async/use_coroutine.md index 537e27ea4..caffb6bbc 100644 --- a/docs/guide/src/en/async/use_coroutine.md +++ b/docs/guide/src/en/async/use_coroutine.md @@ -146,7 +146,7 @@ async fn editor_service(rx: UnboundedReceiver) { We can combine coroutines with [Fermi](https://docs.rs/fermi/latest/fermi/index.html) to emulate Redux Toolkit's Thunk system with much less headache. This lets us store all of our app's state *within* a task and then simply update the "view" values stored in Atoms. It cannot be understated how powerful this technique is: we get all the perks of native Rust tasks with the optimizations and ergonomics of global state. This means your *actual* state does not need to be tied up in a system like Fermi or Redux – the only Atoms that need to exist are those that are used to drive the display/UI. ```rust -static USERNAME: Atom = |_| "default".to_string(); +static USERNAME: Atom = Atom(|_| "default".to_string()); fn app(cx: Scope) -> Element { let atoms = use_atom_root(cx); @@ -159,7 +159,7 @@ fn app(cx: Scope) -> Element { } fn Banner(cx: Scope) -> Element { - let username = use_read(cx, USERNAME); + let username = use_read(cx, &USERNAME); cx.render(rsx!{ h1 { "Welcome back, {username}" } @@ -177,8 +177,8 @@ enum SyncAction { } async fn sync_service(mut rx: UnboundedReceiver, atoms: AtomRoot) { - let username = atoms.write(USERNAME); - let errors = atoms.write(ERRORS); + let username = atoms.write(&USERNAME); + let errors = atoms.write(&ERRORS); while let Ok(msg) = rx.next().await { match msg { diff --git a/docs/guide/src/en/best_practices/error_handling.md b/docs/guide/src/en/best_practices/error_handling.md index afbb45417..992926054 100644 --- a/docs/guide/src/en/best_practices/error_handling.md +++ b/docs/guide/src/en/best_practices/error_handling.md @@ -118,14 +118,14 @@ enum InputError { TooShort, } -static INPUT_ERROR: Atom = |_| InputError::None; +static INPUT_ERROR: Atom = Atom(|_| InputError::None); ``` Then, in our top level component, we want to explicitly handle the possible error state for this part of the tree. ```rust fn TopLevel(cx: Scope) -> Element { - let error = use_read(cx, INPUT_ERROR); + let error = use_read(cx, &INPUT_ERROR); match error { 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 fn Commandline(cx: Scope) -> Element { - let set_error = use_set(cx, INPUT_ERROR); + let set_error = use_set(cx, &INPUT_ERROR); cx.render(rsx!{ input { diff --git a/docs/guide/src/pt-br/async/use_coroutine.md b/docs/guide/src/pt-br/async/use_coroutine.md index cd7cbff11..b6fd71c6c 100644 --- a/docs/guide/src/pt-br/async/use_coroutine.md +++ b/docs/guide/src/pt-br/async/use_coroutine.md @@ -105,7 +105,7 @@ async fn editor_service(rx: UnboundedReceiver) { Podemos combinar corrotinas com `Fermi` para emular o sistema `Thunk` do **Redux Toolkit** com muito menos dor de cabeça. Isso nos permite armazenar todo o estado do nosso aplicativo _dentro_ de uma tarefa e, em seguida, simplesmente atualizar os valores de "visualização" armazenados em `Atoms`. Não pode ser subestimado o quão poderosa é essa técnica: temos todas as vantagens das tarefas nativas do Rust com as otimizações e ergonomia do estado global. Isso significa que seu estado _real_ não precisa estar vinculado a um sistema como `Fermi` ou `Redux` – os únicos `Atoms` que precisam existir são aqueles que são usados para controlar a interface. ```rust -static USERNAME: Atom = |_| "default".to_string(); +static USERNAME: Atom = Atom(|_| "default".to_string()); fn app(cx: Scope) -> Element { let atoms = use_atom_root(cx); @@ -118,7 +118,7 @@ fn app(cx: Scope) -> Element { } fn Banner(cx: Scope) -> Element { - let username = use_read(cx, USERNAME); + let username = use_read(cx, &USERNAME); cx.render(rsx!{ h1 { "Welcome back, {username}" } @@ -134,8 +134,8 @@ enum SyncAction { } async fn sync_service(mut rx: UnboundedReceiver, atoms: AtomRoot) { - let username = atoms.write(USERNAME); - let errors = atoms.write(ERRORS); + let username = atoms.write(&USERNAME); + let errors = atoms.write(&ERRORS); while let Ok(msg) = rx.next().await { match msg { diff --git a/docs/guide/src/pt-br/best_practices/error_handling.md b/docs/guide/src/pt-br/best_practices/error_handling.md index 938322234..e0a7be509 100644 --- a/docs/guide/src/pt-br/best_practices/error_handling.md +++ b/docs/guide/src/pt-br/best_practices/error_handling.md @@ -113,14 +113,14 @@ enum InputError { TooShort, } -static INPUT_ERROR: Atom = |_| InputError::None; +static INPUT_ERROR: Atom = Atom(|_| InputError::None); ``` Então, em nosso componente de nível superior, queremos tratar explicitamente o possível estado de erro para esta parte da árvore. ```rust fn TopLevel(cx: Scope) -> Element { - let error = use_read(cx, INPUT_ERROR); + let error = use_read(cx, &INPUT_ERROR); match error { 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 fn Commandline(cx: Scope) -> Element { - let set_error = use_set(cx, INPUT_ERROR); + let set_error = use_set(cx, &INPUT_ERROR); cx.render(rsx!{ input { diff --git a/examples/fermi.rs b/examples/fermi.rs index 001890892..a15fc807f 100644 --- a/examples/fermi.rs +++ b/examples/fermi.rs @@ -7,11 +7,11 @@ fn main() { dioxus_desktop::launch(app) } -static NAME: Atom = |_| "world".to_string(); +static NAME: Atom = Atom(|_| "world".to_string()); fn app(cx: Scope) -> Element { use_init_atom_root(cx); - let name = use_read(cx, NAME); + let name = use_read(cx, &NAME); cx.render(rsx! { div { "hello {name}!" } @@ -21,7 +21,7 @@ fn app(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! { button { @@ -31,10 +31,10 @@ fn Child(cx: Scope) -> Element { }) } -static NAMES: AtomRef> = |_| vec!["world".to_string()]; +static NAMES: AtomRef> = AtomRef(|_| vec!["world".to_string()]); fn ChildWithRef(cx: Scope) -> Element { - let names = use_atom_ref(cx, NAMES); + let names = use_atom_ref(cx, &NAMES); cx.render(rsx! { div { diff --git a/packages/fermi/README.md b/packages/fermi/README.md index f04ec5961..f8192bbc7 100644 --- a/packages/fermi/README.md +++ b/packages/fermi/README.md @@ -1,4 +1,3 @@ -

Fermi ⚛

@@ -6,7 +5,6 @@

- ------ +--- Fermi is a global state management solution for Dioxus that's as easy as `use_state`. Inspired by atom-based state management solutions, all state in Fermi starts as an `atom`: ```rust, ignore -static NAME: Atom<&str> = |_| "Dioxus"; +static NAME: Atom<&str> = Atom(|_| "Dioxus"); ``` From anywhere in our app, we can read the value of our atom: -```rust, ignores +```rust, ignore fn NameCard(cx: Scope) -> Element { - let name = use_read(cx, NAME); + let name = use_read(cx, &NAME); cx.render(rsx!{ h1 { "Hello, {name}"} }) } ``` @@ -53,7 +51,7 @@ We can also set the value of our atom, also from anywhere in our app: ```rust, ignore fn NameCard(cx: Scope) -> Element { - let set_name = use_set(cx, NAME); + let set_name = use_set(cx, &NAME); cx.render(rsx!{ button { onclick: move |_| set_name("Fermi"), @@ -66,10 +64,10 @@ fn NameCard(cx: Scope) -> Element { If needed, we can update the atom's value, based on itself: ```rust, ignore -static COUNT: Atom = |_| 0; +static COUNT: Atom = Atom(|_| 0); fn Counter(cx: Scope) -> Element { - let mut count = use_atom_state(cx, COUNT); + let mut count = use_atom_state(cx, &COUNT); cx.render(rsx!{ p { @@ -86,6 +84,7 @@ fn Counter(cx: Scope) -> Element { It's that simple! ## Installation + Fermi is currently under construction, so you have to use the `master` branch to get started. ```toml @@ -93,10 +92,10 @@ Fermi is currently under construction, so you have to use the `master` branch to fermi = { git = "https://github.com/dioxuslabs/dioxus" } ``` - ## Running examples The examples here use Dioxus Desktop to showcase their functionality. To run an example, use + ```sh $ cargo run --example fermi ``` @@ -104,6 +103,7 @@ $ cargo run --example fermi ## Features Broadly our feature set required to be released includes: + - [x] Support for Atoms - [x] Support for AtomRef (for values that aren't `Clone`) - [ ] Support for Atom Families diff --git a/packages/fermi/src/atoms/atom.rs b/packages/fermi/src/atoms/atom.rs index 2b99174b5..bba1f532f 100644 --- a/packages/fermi/src/atoms/atom.rs +++ b/packages/fermi/src/atoms/atom.rs @@ -1,21 +1,21 @@ use crate::{AtomId, AtomRoot, Readable, Writable}; -pub type Atom = fn(AtomBuilder) -> T; +pub struct Atom(pub fn(AtomBuilder) -> T); pub struct AtomBuilder; -impl Readable for Atom { +impl Readable for &'static Atom { fn read(&self, _root: AtomRoot) -> Option { todo!() } fn init(&self) -> V { - (*self)(AtomBuilder) + self.0(AtomBuilder) } fn unique_id(&self) -> AtomId { - *self as *const () + *self as *const Atom as *const () } } -impl Writable for Atom { +impl Writable for &'static Atom { fn write(&self, _root: AtomRoot, _value: V) { todo!() } @@ -23,6 +23,22 @@ impl Writable for Atom { #[test] fn atom_compiles() { - static TEST_ATOM: Atom<&str> = |_| "hello"; - dbg!(TEST_ATOM.init()); + static TEST_ATOM: Atom<&str> = Atom(|_| "hello"); + dbg!((&TEST_ATOM).init()); +} + +#[test] +fn atom_is_unique() { + static TEST_ATOM_1: Atom<&str> = Atom(|_| "hello"); + static TEST_ATOM_2: Atom<&str> = Atom(|_| "hello"); + assert_eq!((&TEST_ATOM_1).unique_id(), (&TEST_ATOM_1).unique_id()); + assert_ne!((&TEST_ATOM_1).unique_id(), (&TEST_ATOM_2).unique_id()); +} + +#[test] +fn atom_is_unique_2() { + struct S(String); + static TEST_ATOM_1: Atom> = Atom(|_| Vec::new()); + static TEST_ATOM_2: Atom> = Atom(|_| Vec::new()); + assert_ne!((&TEST_ATOM_1).unique_id(), (&TEST_ATOM_2).unique_id()); } diff --git a/packages/fermi/src/atoms/atomfamily.rs b/packages/fermi/src/atoms/atomfamily.rs index 60fc22bcb..45f6b8feb 100644 --- a/packages/fermi/src/atoms/atomfamily.rs +++ b/packages/fermi/src/atoms/atomfamily.rs @@ -2,23 +2,23 @@ use crate::{AtomId, AtomRoot, Readable, Writable}; use im_rc::HashMap as ImMap; pub struct AtomFamilyBuilder; -pub type AtomFamily = fn(AtomFamilyBuilder) -> ImMap; +pub struct AtomFamily(pub fn(AtomFamilyBuilder) -> ImMap); -impl Readable> for AtomFamily { +impl Readable> for &'static AtomFamily { fn read(&self, _root: AtomRoot) -> Option> { todo!() } fn init(&self) -> ImMap { - (*self)(AtomFamilyBuilder) + self.0(AtomFamilyBuilder) } fn unique_id(&self) -> AtomId { - *self as *const () + *self as *const AtomFamily as *const () } } -impl Writable> for AtomFamily { +impl Writable> for &'static AtomFamily { fn write(&self, _root: AtomRoot, _value: ImMap) { todo!() } diff --git a/packages/fermi/src/atoms/atomref.rs b/packages/fermi/src/atoms/atomref.rs index 59e412031..5e863ade2 100644 --- a/packages/fermi/src/atoms/atomref.rs +++ b/packages/fermi/src/atoms/atomref.rs @@ -2,24 +2,24 @@ use crate::{AtomId, AtomRoot, Readable}; use std::cell::RefCell; pub struct AtomRefBuilder; -pub type AtomRef = fn(AtomRefBuilder) -> T; +pub struct AtomRef(pub fn(AtomRefBuilder) -> T); -impl Readable> for AtomRef { +impl Readable> for &'static AtomRef { fn read(&self, _root: AtomRoot) -> Option> { todo!() } fn init(&self) -> RefCell { - RefCell::new((*self)(AtomRefBuilder)) + RefCell::new(self.0(AtomRefBuilder)) } fn unique_id(&self) -> AtomId { - *self as *const () + *self as *const AtomRef as *const () } } #[test] fn atom_compiles() { - static TEST_ATOM: AtomRef> = |_| vec![]; - dbg!(TEST_ATOM.init()); + static TEST_ATOM: AtomRef> = AtomRef(|_| vec![]); + dbg!((&TEST_ATOM).init()); } diff --git a/packages/fermi/src/hooks/atom_ref.rs b/packages/fermi/src/hooks/atom_ref.rs index 95732c491..74dd9268b 100644 --- a/packages/fermi/src/hooks/atom_ref.rs +++ b/packages/fermi/src/hooks/atom_ref.rs @@ -13,7 +13,10 @@ use std::{ /// /// /// -pub fn use_atom_ref(cx: &ScopeState, atom: AtomRef) -> &UseAtomRef { +pub fn use_atom_ref<'a, T: 'static>( + cx: &'a ScopeState, + atom: &'static AtomRef, +) -> &'a UseAtomRef { let root = use_atom_root(cx); &cx.use_hook(|| { diff --git a/packages/fermi/src/hooks/state.rs b/packages/fermi/src/hooks/state.rs index 15310cff2..071b18059 100644 --- a/packages/fermi/src/hooks/state.rs +++ b/packages/fermi/src/hooks/state.rs @@ -19,7 +19,7 @@ use std::{ /// static COUNT: Atom = |_| 0; /// /// fn Example(cx: Scope) -> Element { -/// let mut count = use_atom_state(cx, COUNT); +/// let mut count = use_atom_state(cx, &COUNT); /// /// cx.render(rsx! { /// div { From 8a2f9f3fcb37e63b64a92a6ce01511b5137dd122 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 28 Jun 2023 00:15:00 +0200 Subject: [PATCH 03/20] feat(docs): Improved shared state, use_effect and use_memo docs --- docs/guide/src/en/SUMMARY.md | 2 + docs/guide/src/en/async/use_effect.md | 32 +++++++++++ .../guide/src/en/interactivity/memoization.md | 23 ++++++++ .../src/en/interactivity/sharing_state.md | 13 +++-- examples/shared_state.rs | 54 +++++++++++++++++++ packages/hooks/src/useeffect.rs | 25 ++++++--- packages/hooks/src/usememo.rs | 28 ++++++---- 7 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 docs/guide/src/en/async/use_effect.md create mode 100644 docs/guide/src/en/interactivity/memoization.md create mode 100644 examples/shared_state.rs diff --git a/docs/guide/src/en/SUMMARY.md b/docs/guide/src/en/SUMMARY.md index 62ef5adf7..a44439200 100644 --- a/docs/guide/src/en/SUMMARY.md +++ b/docs/guide/src/en/SUMMARY.md @@ -21,10 +21,12 @@ - [Hooks & Component State](interactivity/hooks.md) - [User Input](interactivity/user_input.md) - [Sharing State](interactivity/sharing_state.md) + - [Memoization](interactivity/memoization.md) - [Custom Hooks](interactivity/custom_hooks.md) - [Dynamic Rendering](interactivity/dynamic_rendering.md) - [Routing](interactivity/router.md) - [Async](async/index.md) + - [UseEffect](async/use_effect.md) - [UseFuture](async/use_future.md) - [UseCoroutine](async/use_coroutine.md) - [Spawning Futures](async/spawn.md) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md new file mode 100644 index 000000000..711492500 --- /dev/null +++ b/docs/guide/src/en/async/use_effect.md @@ -0,0 +1,32 @@ +# UseEffect + +[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) provides a future that executes after the hooks have been applied. + +Whenever the hooks dependencies change, the future will be re-evaluated. This is useful to syncrhonize with external events. + +If a future is pending when the dependencies change, the previous future will be allowed to continue + +> The `dependencies` is tuple of references to values that are `PartialEq + Clone`. + +```rust, no_run +#[inline_props] +fn Profile(cx: Scope, id: &str) -> Element { + let name = use_state(cx, || "Default name"); + + // Only fetch the user data when the id changes. + use_effect(cx, (id,), |(id,)| async move { + let user = fetch_user(id).await; + name.set(user.name); + }); + + render!( + p { "{name}" } + ) +} + +fn app(cx: Scope) -> Element { + render!( + Profile { id: "dioxusLabs" } + ) +} +``` diff --git a/docs/guide/src/en/interactivity/memoization.md b/docs/guide/src/en/interactivity/memoization.md new file mode 100644 index 000000000..cc67c02c4 --- /dev/null +++ b/docs/guide/src/en/interactivity/memoization.md @@ -0,0 +1,23 @@ +# Memoization + +[`use_memo`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_memo.html) let's you memorize values and therefore save computation time. This is useful for expensive calculations. + +```rust, no_run +#[inline_props] +fn Calculator(cx: Scope, number: usize) -> Element { + let bigger_number = use_memo(cx, (number,), |(number,)| { + // This will only be calculated when `number` has changed. + number * 100 + }): + + render!( + p { "{bigger_number}" } + ) +} + +fn app(cx: Scope) -> Element { + render!( + Calculator { number: 0 } + ) +} +``` diff --git a/docs/guide/src/en/interactivity/sharing_state.md b/docs/guide/src/en/interactivity/sharing_state.md index 75ace8973..3f9b3cdc2 100644 --- a/docs/guide/src/en/interactivity/sharing_state.md +++ b/docs/guide/src/en/interactivity/sharing_state.md @@ -11,6 +11,7 @@ Suppose we want to build a meme editor. We want to have an input to edit the mem > Of course, in this simple example, we could write everything in one component – but it is better to split everything out in smaller components to make the code more reusable, maintainable, and performant (this is even more important for larger, complex apps). We start with a `Meme` component, responsible for rendering a meme with a given caption: + ```rust {{#include ../../../examples/meme_editor.rs:meme_component}} ``` @@ -24,12 +25,14 @@ We also create a caption editor, completely decoupled from the meme. The caption ``` Finally, a third component will render the other two as children. It will be responsible for keeping the state and passing down the relevant props. + ```rust {{#include ../../../examples/meme_editor.rs:meme_editor}} ``` + ![Meme Editor Screenshot: An old plastic skeleton sitting on a park bench. Caption: "me waiting for a language feature"](./images/meme_editor_screenshot.png) -## Using Context +## Using Shared State Sometimes, some state needs to be shared between multiple components far down the tree, and passing it down through props is very inconvenient. @@ -39,7 +42,7 @@ Suppose now that we want to implement a dark mode toggle for our app. To achieve Now, we could write another `use_state` in the top component, and pass `is_dark_mode` down to every component through props. But think about what will happen as the app grows in complexity – almost every component that renders any CSS is going to need to know if dark mode is enabled or not – so they'll all need the same dark mode prop. And every parent component will need to pass it down to them. Imagine how messy and verbose that would get, especially if we had components several levels deep! -Dioxus offers a better solution than this "prop drilling" – providing context. The [`use_context_provider`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context_provider.html) hook is similar to `use_ref`, but it makes it available through [`use_context`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context.html) for all children components. +Dioxus offers a better solution than this "prop drilling" – providing context. The [`use_shared_state_provider`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_shared_state_provider.html) hook is similar to `use_ref`, but it makes it available through [`use_shared_state`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_shared_state.html) for all children components. First, we have to create a struct for our dark mode configuration: @@ -48,19 +51,21 @@ First, we have to create a struct for our dark mode configuration: ``` Now, in a top-level component (like `App`), we can provide the `DarkMode` context to all children components: + ```rust {{#include ../../../examples/meme_editor_dark_mode.rs:context_provider}} ``` As a result, any child component of `App` (direct or not), can access the `DarkMode` context. + ```rust {{#include ../../../examples/meme_editor_dark_mode.rs:use_context}} ``` -> `use_context` returns `Option>` here. If the context has been provided, the value is `Some(UseSharedState)`, which you can call `.read` or `.write` on, similarly to `UseRef`. Otherwise, the value is `None`. +> `use_shared_state` returns `Option>` here. If the context has been provided, the value is `Some(UseSharedState)`, which you can call `.read` or `.write` on, similarly to `UseRef`. Otherwise, the value is `None`. For example, here's how we would implement the dark mode toggle, which both reads the context (to determine what color it should render) and writes to it (to toggle dark mode): + ```rust {{#include ../../../examples/meme_editor_dark_mode.rs:toggle}} ``` - diff --git a/examples/shared_state.rs b/examples/shared_state.rs new file mode 100644 index 000000000..1072ce50d --- /dev/null +++ b/examples/shared_state.rs @@ -0,0 +1,54 @@ +#![allow(non_snake_case)] + +use dioxus::prelude::*; + +fn main() { + dioxus_desktop::launch(App); +} + +struct DarkMode(bool); + +#[rustfmt::skip] +pub fn App(cx: Scope) -> Element { + use_shared_state_provider(cx, || DarkMode(false)); + + render!( + DarkModeToggle {}, + AppBody {} + ) +} + +pub fn DarkModeToggle(cx: Scope) -> Element { + let dark_mode = use_shared_state::(cx).unwrap(); + + let style = if dark_mode.read().0 { + "color:white" + } else { + "" + }; + + cx.render(rsx!(label { + style: "{style}", + "Dark Mode", + input { + r#type: "checkbox", + oninput: move |event| { + let is_enabled = event.value == "true"; + dark_mode.write().0 = is_enabled; + }, + }, + })) +} + +fn AppBody(cx: Scope) -> Element { + let dark_mode = use_shared_state::(cx).unwrap(); + + let is_dark_mode = dark_mode.read().0; + let answer = if is_dark_mode { "Yes" } else { "No" }; + + render!( + p { + "Is Dark mode enabled? {answer}" + } + ) +} diff --git a/packages/hooks/src/useeffect.rs b/packages/hooks/src/useeffect.rs index f9ef70b84..b52b89c3c 100644 --- a/packages/hooks/src/useeffect.rs +++ b/packages/hooks/src/useeffect.rs @@ -9,17 +9,30 @@ use crate::UseFutureDep; /// If a future is pending when the dependencies change, the previous future /// will be allowed to continue /// -/// - dependencies: a tuple of references to values that are PartialEq + Clone +/// - dependencies: a tuple of references to values that are `PartialEq` + `Clone` /// /// ## Examples /// -/// ```rust, ignore +/// ```rust, no_run /// /// #[inline_props] -/// fn app(cx: Scope, name: &str) -> Element { -/// use_effect(cx, (name,), |(name,)| async move { -/// set_title(name); -/// })) +/// fn Profile(cx: Scope, id: &str) -> Element { +/// let name = use_state(cx, || "Default name"); +/// +/// use_effect(cx, (id,), |(id,)| async move { +/// let user = fetch_user(id).await; +/// name.set(user.name); +/// }); +/// +/// render!( +/// p { "{name}" } +/// ) +/// } +/// +/// fn app(cx: Scope) -> Element { +/// render!( +/// Profile { id: "dioxusLabs" } +/// ) /// } /// ``` pub fn use_effect(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F) diff --git a/packages/hooks/src/usememo.rs b/packages/hooks/src/usememo.rs index 8cc5540e6..f99d1e893 100644 --- a/packages/hooks/src/usememo.rs +++ b/packages/hooks/src/usememo.rs @@ -2,21 +2,31 @@ use dioxus_core::ScopeState; use crate::UseFutureDep; -/// A hook that provides a callback that executes after the hooks have been applied +/// A hook that provides a callback that executes if the dependencies change. +/// This is useful to avoid running computation-expensive calculations even when the data doesn't change. /// -/// Whenever the hooks dependencies change, the callback will be re-evaluated. -/// -/// - dependencies: a tuple of references to values that are PartialEq + Clone +/// - dependencies: a tuple of references to values that are `PartialEq` + `Clone` /// /// ## Examples /// -/// ```rust, ignore +/// ```rust, no_run /// /// #[inline_props] -/// fn app(cx: Scope, name: &str) -> Element { -/// use_memo(cx, (name,), |(name,)| { -/// expensive_computation(name); -/// })) +/// fn Calculator(cx: Scope, number: usize) -> Element { +/// let bigger_number = use_memo(cx, (number,), |(number,)| { +/// // This will only be calculated when `number` has changed. +/// number * 100 +/// }): +/// +/// render!( +/// p { "{bigger_number}" } +/// ) +/// } +/// +/// fn app(cx: Scope) -> Element { +/// render!( +/// Calculator { number: 0 } +/// ) /// } /// ``` pub fn use_memo(cx: &ScopeState, dependencies: D, callback: impl FnOnce(D::Out) -> T) -> &T From a3978d60a898088c68376c51b39c30452c78b854 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 28 Jun 2023 00:17:36 +0200 Subject: [PATCH 04/20] update --- examples/shared_state.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/examples/shared_state.rs b/examples/shared_state.rs index 1072ce50d..6cd4d874e 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -21,23 +21,13 @@ pub fn App(cx: Scope) -> Element { pub fn DarkModeToggle(cx: Scope) -> Element { let dark_mode = use_shared_state::(cx).unwrap(); - let style = if dark_mode.read().0 { - "color:white" - } else { - "" - }; - - cx.render(rsx!(label { - style: "{style}", - "Dark Mode", - input { - r#type: "checkbox", - oninput: move |event| { - let is_enabled = event.value == "true"; - dark_mode.write().0 = is_enabled; - }, + render!(input { + r#type: "checkbox", + oninput: move |event| { + let is_enabled = event.value == "true"; + dark_mode.write().0 = is_enabled; }, - })) + }) } fn AppBody(cx: Scope) -> Element { From bf0247a7dc84aeafd2f91656f1e7bcc5b19298de Mon Sep 17 00:00:00 2001 From: marc2332 Date: Wed, 28 Jun 2023 20:01:28 +0200 Subject: [PATCH 05/20] tweaks --- docs/guide/src/en/async/use_effect.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index 711492500..8b82828db 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -2,11 +2,14 @@ [`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) provides a future that executes after the hooks have been applied. -Whenever the hooks dependencies change, the future will be re-evaluated. This is useful to syncrhonize with external events. +Whenever the hooks [dependencies](#dependencies) change, the future will be re-evaluated. This is useful to syncrhonize with external events. -If a future is pending when the dependencies change, the previous future will be allowed to continue -> The `dependencies` is tuple of references to values that are `PartialEq + Clone`. +## Dependencies + +You might want to call `use_effect` only when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. + +Example: ```rust, no_run #[inline_props] @@ -29,4 +32,4 @@ fn app(cx: Scope) -> Element { Profile { id: "dioxusLabs" } ) } -``` +``` \ No newline at end of file From d00b10e83ec9b75eb776b6093686e661d50d23b3 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 29 Jun 2023 14:14:29 +0200 Subject: [PATCH 06/20] tweaks --- docs/guide/src/en/async/use_effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index 8b82828db..4980e2d20 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -7,7 +7,7 @@ Whenever the hooks [dependencies](#dependencies) change, the future will be re-e ## Dependencies -You might want to call `use_effect` only when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. +You can make the future re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. Example: From fb2669c3be5800ff9ab9a8ca680283021bdda445 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sun, 2 Jul 2023 22:41:00 +0200 Subject: [PATCH 07/20] improved shared state example --- examples/shared_state.rs | 89 +++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/examples/shared_state.rs b/examples/shared_state.rs index 6cd4d874e..2d5f6f125 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -1,44 +1,77 @@ #![allow(non_snake_case)] +use std::collections::HashMap; + use dioxus::prelude::*; fn main() { dioxus_desktop::launch(App); } -struct DarkMode(bool); +#[derive(Default)] +struct CoolData { + data: HashMap, +} + +impl CoolData { + pub fn new(data: HashMap) -> Self { + Self { data } + } + + pub fn view(&self, id: &usize) -> Option<&String> { + self.data.get(id) + } + + pub fn set(&mut self, id: usize, data: String) { + self.data.insert(id, data); + } +} #[rustfmt::skip] pub fn App(cx: Scope) -> Element { - use_shared_state_provider(cx, || DarkMode(false)); + use_shared_state_provider(cx, || CoolData::new(HashMap::from([ + (0, "Hello, World!".to_string()), + (1, "Dioxus is amazing!".to_string()) + ]))); render!( - DarkModeToggle {}, - AppBody {} - ) -} - -pub fn DarkModeToggle(cx: Scope) -> Element { - let dark_mode = use_shared_state::(cx).unwrap(); - - render!(input { - r#type: "checkbox", - oninput: move |event| { - let is_enabled = event.value == "true"; - dark_mode.write().0 = is_enabled; - }, - }) -} - -fn AppBody(cx: Scope) -> Element { - let dark_mode = use_shared_state::(cx).unwrap(); - - let is_dark_mode = dark_mode.read().0; - let answer = if is_dark_mode { "Yes" } else { "No" }; - - render!( - p { - "Is Dark mode enabled? {answer}" + DataEditor { + id: 0 + } + DataEditor { + id: 1 + } + DataView { + id: 0 + } + DataView { + id: 1 } ) } + +#[inline_props] +fn DataEditor(cx: Scope, id: usize) -> Element { + let cool_data = use_shared_state::(cx).unwrap().read(); + + let my_data = &cool_data.view(&id).unwrap(); + + render!(p { + "{my_data}" + }) +} + +#[inline_props] +fn DataView(cx: Scope, id: usize) -> Element { + let cool_data = use_shared_state::(cx).unwrap(); + + let oninput = |e: FormEvent| cool_data.write().set(*id, e.value.clone()); + + let cool_data = cool_data.read(); + let my_data = &cool_data.view(&id).unwrap(); + + render!(input { + oninput: oninput, + value: "{my_data}" + }) +} From 052ae145bfb9fda38f72b43341d6059255282a61 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Sun, 2 Jul 2023 23:00:26 +0200 Subject: [PATCH 08/20] updated docs --- docs/guide/src/en/async/use_effect.md | 22 ++++++++++--------- .../guide/src/en/interactivity/memoization.md | 8 ++----- packages/hooks/src/useeffect.rs | 21 ++++++++++-------- packages/hooks/src/usememo.rs | 8 ++----- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index 4980e2d20..bd73453a9 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -4,7 +4,6 @@ Whenever the hooks [dependencies](#dependencies) change, the future will be re-evaluated. This is useful to syncrhonize with external events. - ## Dependencies You can make the future re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. @@ -13,23 +12,26 @@ Example: ```rust, no_run #[inline_props] -fn Profile(cx: Scope, id: &str) -> Element { - let name = use_state(cx, || "Default name"); +fn Profile(cx: Scope, id: usize) -> Element { + let name = use_state(cx, || None); // Only fetch the user data when the id changes. - use_effect(cx, (id,), |(id,)| async move { - let user = fetch_user(id).await; - name.set(user.name); + use_effect(cx, (id,), |(id,)| { + to_owned![name]; + async move { + let user = fetch_user(id).await; + name.set(user.name); + } }); + let name = name.get().clone().unwrap_or("Loading...".to_string()); + render!( p { "{name}" } ) } fn app(cx: Scope) -> Element { - render!( - Profile { id: "dioxusLabs" } - ) + render!(Profile { id: 0 }) } -``` \ No newline at end of file +``` diff --git a/docs/guide/src/en/interactivity/memoization.md b/docs/guide/src/en/interactivity/memoization.md index cc67c02c4..72a709bcc 100644 --- a/docs/guide/src/en/interactivity/memoization.md +++ b/docs/guide/src/en/interactivity/memoization.md @@ -8,16 +8,12 @@ fn Calculator(cx: Scope, number: usize) -> Element { let bigger_number = use_memo(cx, (number,), |(number,)| { // This will only be calculated when `number` has changed. number * 100 - }): - + }); render!( p { "{bigger_number}" } ) } - fn app(cx: Scope) -> Element { - render!( - Calculator { number: 0 } - ) + render!(Calculator { number: 0 }) } ``` diff --git a/packages/hooks/src/useeffect.rs b/packages/hooks/src/useeffect.rs index b52b89c3c..aec1f9c9a 100644 --- a/packages/hooks/src/useeffect.rs +++ b/packages/hooks/src/useeffect.rs @@ -14,25 +14,28 @@ use crate::UseFutureDep; /// ## Examples /// /// ```rust, no_run -/// /// #[inline_props] -/// fn Profile(cx: Scope, id: &str) -> Element { -/// let name = use_state(cx, || "Default name"); +/// fn Profile(cx: Scope, id: usize) -> Element { +/// let name = use_state(cx, || None); /// -/// use_effect(cx, (id,), |(id,)| async move { -/// let user = fetch_user(id).await; -/// name.set(user.name); +/// // Only fetch the user data when the id changes. +/// use_effect(cx, (id,), |(id,)| { +/// to_owned![name]; +/// async move { +/// let user = fetch_user(id).await; +/// name.set(user.name); +/// } /// }); /// +/// let name = name.get().clone().unwrap_or("Loading...".to_string()); +/// /// render!( /// p { "{name}" } /// ) /// } /// /// fn app(cx: Scope) -> Element { -/// render!( -/// Profile { id: "dioxusLabs" } -/// ) +/// render!(Profile { id: 0 }) /// } /// ``` pub fn use_effect(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F) diff --git a/packages/hooks/src/usememo.rs b/packages/hooks/src/usememo.rs index f99d1e893..d3d991ccc 100644 --- a/packages/hooks/src/usememo.rs +++ b/packages/hooks/src/usememo.rs @@ -16,17 +16,13 @@ use crate::UseFutureDep; /// let bigger_number = use_memo(cx, (number,), |(number,)| { /// // This will only be calculated when `number` has changed. /// number * 100 -/// }): -/// +/// }); /// render!( /// p { "{bigger_number}" } /// ) /// } -/// /// fn app(cx: Scope) -> Element { -/// render!( -/// Calculator { number: 0 } -/// ) +/// render!(Calculator { number: 0 }) /// } /// ``` pub fn use_memo(cx: &ScopeState, dependencies: D, callback: impl FnOnce(D::Out) -> T) -> &T From 1f3f72edc30e98fc0e61d1f940e49828beb1dc76 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 6 Jul 2023 17:23:37 +0200 Subject: [PATCH 09/20] updated --- docs/guide/src/en/async/use_effect.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index bd73453a9..f509921fc 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -1,14 +1,12 @@ # UseEffect -[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) provides a future that executes after the hooks have been applied. - -Whenever the hooks [dependencies](#dependencies) change, the future will be re-evaluated. This is useful to syncrhonize with external events. +[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) let's you run a callback that returns a future, only it's [dependencies](#dependencies) change. This is useful to syncrhonize with external events. ## Dependencies -You can make the future re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. +You can make the callback re-run when some value changes. For example, you might want to fetch a user's data only when the user id changes. You can provide a tuple of "dependencies" to the hook. It will automatically re-run it when any of those dependencies change. -Example: +## Example ```rust, no_run #[inline_props] @@ -24,6 +22,12 @@ fn Profile(cx: Scope, id: usize) -> Element { } }); + // Because the dependencies are empty, this will only run once. + // An empty tuple is always equal to an empty tuple. + use_effect(cx, (), |()| async move { + println!("Hello, World!"); + }); + let name = name.get().clone().unwrap_or("Loading...".to_string()); render!( From 39bce476aedc99f431114e667aa430bd0938d605 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 13 Jul 2023 19:06:43 +0200 Subject: [PATCH 10/20] tweaks --- docs/guide/src/en/async/use_effect.md | 2 +- examples/test.rs | 63 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 examples/test.rs diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index f509921fc..9dc5a8739 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -1,6 +1,6 @@ # UseEffect -[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) let's you run a callback that returns a future, only it's [dependencies](#dependencies) change. This is useful to syncrhonize with external events. +[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) lets you run a callback that returns a future, which will be re-run when it's [dependencies](#dependencies) change. This is useful to syncrhonize with external events. ## Dependencies diff --git a/examples/test.rs b/examples/test.rs new file mode 100644 index 000000000..546934481 --- /dev/null +++ b/examples/test.rs @@ -0,0 +1,63 @@ +use dioxus::prelude::*; +use dioxus_router::*; + +fn main() { + // init debug tool for WebAssembly + wasm_logger::init(wasm_logger::Config::default()); + console_error_panic_hook::set_once(); + + dioxus_web::launch(app); +} + +fn Works1(cx: Scope) -> Element { + render!( + p { + "this is 1" + } + a { + href: "#section", + "section" + } + Link { + to: "/2", + p { + "go to 2" + } + } + p { + "{\"AAAA\n\".repeat(999)}" + } + h2 { + id: "section", + "section" + } + ) +} + +fn Works2(cx: Scope) -> Element { + render!( + p { + "this is 2" + Link { + to: "/", + p { + "go to 1" + } + } + ) +} + +fn app(cx: Scope) -> Element { + cx.render(rsx! ( + Router { + Route { + to: "/", + Works1 {} + } + Route { + to: "/2", + Works2 {} + } + } + )) +} \ No newline at end of file From f7dd4190438c3f25fc00502037a1d21d62368002 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 13 Jul 2023 22:37:05 +0200 Subject: [PATCH 11/20] update --- docs/guide/src/en/interactivity/memoization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/interactivity/memoization.md b/docs/guide/src/en/interactivity/memoization.md index 72a709bcc..32ec37ce5 100644 --- a/docs/guide/src/en/interactivity/memoization.md +++ b/docs/guide/src/en/interactivity/memoization.md @@ -1,6 +1,6 @@ # Memoization -[`use_memo`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_memo.html) let's you memorize values and therefore save computation time. This is useful for expensive calculations. +[`use_memo`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_memo.html) let's you memorize values and thus save computation time. This is useful for expensive calculations. ```rust, no_run #[inline_props] From c07006e9a21d2dd60bd0f0e44fbd7090a2a9ce0c Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 13 Jul 2023 22:43:46 +0200 Subject: [PATCH 12/20] :bowtie: --- examples/test.rs | 63 ------------------------------------------------ 1 file changed, 63 deletions(-) delete mode 100644 examples/test.rs diff --git a/examples/test.rs b/examples/test.rs deleted file mode 100644 index 546934481..000000000 --- a/examples/test.rs +++ /dev/null @@ -1,63 +0,0 @@ -use dioxus::prelude::*; -use dioxus_router::*; - -fn main() { - // init debug tool for WebAssembly - wasm_logger::init(wasm_logger::Config::default()); - console_error_panic_hook::set_once(); - - dioxus_web::launch(app); -} - -fn Works1(cx: Scope) -> Element { - render!( - p { - "this is 1" - } - a { - href: "#section", - "section" - } - Link { - to: "/2", - p { - "go to 2" - } - } - p { - "{\"AAAA\n\".repeat(999)}" - } - h2 { - id: "section", - "section" - } - ) -} - -fn Works2(cx: Scope) -> Element { - render!( - p { - "this is 2" - Link { - to: "/", - p { - "go to 1" - } - } - ) -} - -fn app(cx: Scope) -> Element { - cx.render(rsx! ( - Router { - Route { - to: "/", - Works1 {} - } - Route { - to: "/2", - Works2 {} - } - } - )) -} \ No newline at end of file From 17ae835f9e03936033dce0bc9a545a3fbe817ed1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 14 Jul 2023 14:14:51 -0700 Subject: [PATCH 13/20] Try pinning ubuntu version to fix CI --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 604562f6a..84891f403 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -8,7 +8,7 @@ jobs: test: if: github.event.pull_request.draft == false timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 From f2cd6b8af65fc4c8f1e9f109884a2e8696986f15 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 14 Jul 2023 14:54:21 -0700 Subject: [PATCH 14/20] Disable LTO on CLI --- Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d69a74828..b7a6e96e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,8 +115,3 @@ fern = { version = "0.6.0", features = ["colored"] } thiserror = "1.0.30" env_logger = "0.10.0" simple_logger = "4.0.0" - -[profile.release] -opt-level = 3 -lto = true -debug = true From cad19fb59f003d4f16293db3ca8937f7df74cde8 Mon Sep 17 00:00:00 2001 From: Brian Donovan <1938+eventualbuddha@users.noreply.github.com> Date: Fri, 14 Jul 2023 18:38:11 -0400 Subject: [PATCH 15/20] docs: fix typo in `use_effect` description --- docs/guide/src/en/async/use_effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/async/use_effect.md b/docs/guide/src/en/async/use_effect.md index 9dc5a8739..7c20e3589 100644 --- a/docs/guide/src/en/async/use_effect.md +++ b/docs/guide/src/en/async/use_effect.md @@ -1,6 +1,6 @@ # UseEffect -[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) lets you run a callback that returns a future, which will be re-run when it's [dependencies](#dependencies) change. This is useful to syncrhonize with external events. +[`use_effect`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) lets you run a callback that returns a future, which will be re-run when its [dependencies](#dependencies) change. This is useful to syncrhonize with external events. ## Dependencies From d583e1c7506f597830095f40204d063c6c71b850 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 14 Jul 2023 16:34:49 -0700 Subject: [PATCH 16/20] Fix clippy --- examples/shared_state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/shared_state.rs b/examples/shared_state.rs index 2d5f6f125..d129f75d1 100644 --- a/examples/shared_state.rs +++ b/examples/shared_state.rs @@ -54,7 +54,7 @@ pub fn App(cx: Scope) -> Element { fn DataEditor(cx: Scope, id: usize) -> Element { let cool_data = use_shared_state::(cx).unwrap().read(); - let my_data = &cool_data.view(&id).unwrap(); + let my_data = &cool_data.view(id).unwrap(); render!(p { "{my_data}" @@ -68,7 +68,7 @@ fn DataView(cx: Scope, id: usize) -> Element { let oninput = |e: FormEvent| cool_data.write().set(*id, e.value.clone()); let cool_data = cool_data.read(); - let my_data = &cool_data.view(&id).unwrap(); + let my_data = &cool_data.view(id).unwrap(); render!(input { oninput: oninput, From 9cef71b6b98ae071291fe8fb026436bcfa0f1776 Mon Sep 17 00:00:00 2001 From: Brian Donovan <1938+eventualbuddha@users.noreply.github.com> Date: Sat, 15 Jul 2023 12:13:22 -0400 Subject: [PATCH 17/20] fix(cli): improve error message for invalid config (#1200) Previously an invalid `Dioxus.toml` file would yield this error when running e.g. `dx serve`: ``` Error: Failed to load `Dioxus.toml` because: Dioxus.toml parse failed ``` This doesn't give any indication why it failed to parse. This commit updates it to include why the parsing failed, e.g.: ``` Error: Failed to load Dioxus config because: Dioxus.toml missing field `name` for key `application` at line 38 column 1 ``` I initially had it format the message to include `Dioxus.toml:38:1` to be clickable in some terminals, but the location specified didn't always seem particularly relevant to the actual problem so I left it as-is. This also fixes what I believe would be an issue on case-sensitive file systems where if `dioxus.toml` existed it would try to read `Dioxus.toml`. I'm still fairly new to Rust, so the `.as_path()` calls may not be the best way to deal with the fact that the borrow checker wouldn't let me re-use `crate_dir` and `dioxus_conf_file`. I'm open to suggestions! --- packages/cli/src/config.rs | 28 ++++++++++++++++++++-------- packages/cli/src/main.rs | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs index f9117f6e5..79e6c7321 100644 --- a/packages/cli/src/config.rs +++ b/packages/cli/src/config.rs @@ -1,6 +1,9 @@ use crate::error::Result; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DioxusConfig { @@ -19,27 +22,36 @@ fn default_plugin() -> toml::Value { impl DioxusConfig { pub fn load() -> crate::error::Result> { let Ok(crate_dir) = crate::cargo::crate_root() else { return Ok(None); }; + let crate_dir = crate_dir.as_path(); - // we support either `Dioxus.toml` or `Cargo.toml` let Some(dioxus_conf_file) = acquire_dioxus_toml(crate_dir) else { return Ok(None); }; + let dioxus_conf_file = dioxus_conf_file.as_path(); toml::from_str::(&std::fs::read_to_string(dioxus_conf_file)?) - .map_err(|_| crate::Error::Unique("Dioxus.toml parse failed".into())) + .map_err(|err| { + let error_location = dioxus_conf_file + .strip_prefix(crate_dir) + .unwrap_or(dioxus_conf_file) + .display(); + crate::Error::Unique(format!("{error_location} {err}")) + }) .map(Some) } } -fn acquire_dioxus_toml(dir: PathBuf) -> Option { +fn acquire_dioxus_toml(dir: &Path) -> Option { // prefer uppercase - if dir.join("Dioxus.toml").is_file() { - return Some(dir.join("Dioxus.toml")); + let uppercase_conf = dir.join("Dioxus.toml"); + if uppercase_conf.is_file() { + return Some(uppercase_conf); } // lowercase is fine too - if dir.join("dioxus.toml").is_file() { - return Some(dir.join("Dioxus.toml")); + let lowercase_conf = dir.join("dioxus.toml"); + if lowercase_conf.is_file() { + return Some(lowercase_conf); } None diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index f2007aa43..86e90db96 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -14,7 +14,7 @@ async fn main() -> anyhow::Result<()> { set_up_logging(); let _dioxus_config = DioxusConfig::load() - .map_err(|e| anyhow!("Failed to load `Dioxus.toml` because: {e}"))? + .map_err(|e| anyhow!("Failed to load Dioxus config because: {e}"))? .unwrap_or_else(|| { log::warn!("You appear to be creating a Dioxus project from scratch; we will use the default config"); DioxusConfig::default() From 947b7b650ee7b577e7e39a95c41982c11c4f780d Mon Sep 17 00:00:00 2001 From: Brian Donovan <1938+eventualbuddha@users.noreply.github.com> Date: Mon, 17 Jul 2023 12:00:59 -0400 Subject: [PATCH 18/20] chore: rename `playwrite` to `playwright` (#1203) I assume this was a mistake and not intentional. --- .github/workflows/main.yml | 2 +- .gitignore | 4 +-- Cargo.toml | 8 +++--- .../fullstack.spec.js | 0 .../fullstack/.gitignore | 0 .../fullstack/Cargo.toml | 2 +- .../fullstack/src/main.rs | 2 +- .../liveview.spec.js | 0 .../liveview/Cargo.toml | 4 +-- .../liveview/src/main.rs | 2 +- .../web.spec.js | 0 .../web/Cargo.toml | 4 +-- .../web/src/main.rs | 2 +- playwright.config.js | 28 ++++++++++--------- 14 files changed, 30 insertions(+), 28 deletions(-) rename {playwrite-tests => playwright-tests}/fullstack.spec.js (100%) rename {playwrite-tests => playwright-tests}/fullstack/.gitignore (100%) rename {playwrite-tests => playwright-tests}/fullstack/Cargo.toml (93%) rename {playwrite-tests => playwright-tests}/fullstack/src/main.rs (97%) rename {playwrite-tests => playwright-tests}/liveview.spec.js (100%) rename {playwrite-tests => playwright-tests}/liveview/Cargo.toml (77%) rename {playwrite-tests => playwright-tests}/liveview/src/main.rs (96%) rename {playwrite-tests => playwright-tests}/web.spec.js (100%) rename {playwrite-tests => playwright-tests}/web/Cargo.toml (72%) rename {playwrite-tests => playwright-tests}/web/src/main.rs (95%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b44aab843..b37e63aa4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,7 +13,7 @@ on: - lib.rs - Cargo.toml - Makefile.toml - - playwrite-tests/** + - playwright-tests/** pull_request: types: [opened, synchronize, reopened, ready_for_review] diff --git a/.gitignore b/.gitignore index bca0fe28f..b8025753e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /target -/playwrite-tests/web/dist -/playwrite-tests/fullstack/dist +/playwright-tests/web/dist +/playwright-tests/fullstack/dist /dist Cargo.lock .DS_Store diff --git a/Cargo.toml b/Cargo.toml index b7a6e96e9..d849a4b5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,10 +35,10 @@ members = [ # Full project examples "examples/tailwind", "examples/PWA-example", - # Playwrite tests - "playwrite-tests/liveview", - "playwrite-tests/web", - "playwrite-tests/fullstack", + # Playwright tests + "playwright-tests/liveview", + "playwright-tests/web", + "playwright-tests/fullstack", ] exclude = ["examples/mobile_demo"] diff --git a/playwrite-tests/fullstack.spec.js b/playwright-tests/fullstack.spec.js similarity index 100% rename from playwrite-tests/fullstack.spec.js rename to playwright-tests/fullstack.spec.js diff --git a/playwrite-tests/fullstack/.gitignore b/playwright-tests/fullstack/.gitignore similarity index 100% rename from playwrite-tests/fullstack/.gitignore rename to playwright-tests/fullstack/.gitignore diff --git a/playwrite-tests/fullstack/Cargo.toml b/playwright-tests/fullstack/Cargo.toml similarity index 93% rename from playwrite-tests/fullstack/Cargo.toml rename to playwright-tests/fullstack/Cargo.toml index a11bfa461..9cca0d2fa 100644 --- a/playwrite-tests/fullstack/Cargo.toml +++ b/playwright-tests/fullstack/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "dioxus-playwrite-fullstack-test" +name = "dioxus-playwright-fullstack-test" version = "0.1.0" edition = "2021" publish = false diff --git a/playwrite-tests/fullstack/src/main.rs b/playwright-tests/fullstack/src/main.rs similarity index 97% rename from playwrite-tests/fullstack/src/main.rs rename to playwright-tests/fullstack/src/main.rs index 98caadbb6..610c7afcb 100644 --- a/playwrite-tests/fullstack/src/main.rs +++ b/playwright-tests/fullstack/src/main.rs @@ -1,4 +1,4 @@ -// This test is used by playwrite configured in the root of the repo +// This test is used by playwright configured in the root of the repo // Tests: // - Server functions // - SSR diff --git a/playwrite-tests/liveview.spec.js b/playwright-tests/liveview.spec.js similarity index 100% rename from playwrite-tests/liveview.spec.js rename to playwright-tests/liveview.spec.js diff --git a/playwrite-tests/liveview/Cargo.toml b/playwright-tests/liveview/Cargo.toml similarity index 77% rename from playwrite-tests/liveview/Cargo.toml rename to playwright-tests/liveview/Cargo.toml index 8c4cad1ef..173ae1d81 100644 --- a/playwrite-tests/liveview/Cargo.toml +++ b/playwright-tests/liveview/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "dioxus-playwrite-liveview-test" +name = "dioxus-playwright-liveview-test" version = "0.0.1" edition = "2021" -description = "Playwrite test for Dioxus Liveview" +description = "Playwright test for Dioxus Liveview" license = "MIT/Apache-2.0" publish = false diff --git a/playwrite-tests/liveview/src/main.rs b/playwright-tests/liveview/src/main.rs similarity index 96% rename from playwrite-tests/liveview/src/main.rs rename to playwright-tests/liveview/src/main.rs index 42bd9a3e3..d28e3b7fd 100644 --- a/playwrite-tests/liveview/src/main.rs +++ b/playwright-tests/liveview/src/main.rs @@ -1,4 +1,4 @@ -// This test is used by playwrite configured in the root of the repo +// This test is used by playwright configured in the root of the repo use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router}; use dioxus::prelude::*; diff --git a/playwrite-tests/web.spec.js b/playwright-tests/web.spec.js similarity index 100% rename from playwrite-tests/web.spec.js rename to playwright-tests/web.spec.js diff --git a/playwrite-tests/web/Cargo.toml b/playwright-tests/web/Cargo.toml similarity index 72% rename from playwrite-tests/web/Cargo.toml rename to playwright-tests/web/Cargo.toml index b1becd86e..22f4f4096 100644 --- a/playwrite-tests/web/Cargo.toml +++ b/playwright-tests/web/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "dioxus-playwrite-web-test" +name = "dioxus-playwright-web-test" version = "0.0.1" edition = "2021" -description = "Playwrite test for Dioxus Web" +description = "Playwright test for Dioxus Web" license = "MIT/Apache-2.0" publish = false diff --git a/playwrite-tests/web/src/main.rs b/playwright-tests/web/src/main.rs similarity index 95% rename from playwrite-tests/web/src/main.rs rename to playwright-tests/web/src/main.rs index d1881539c..c4850e943 100644 --- a/playwrite-tests/web/src/main.rs +++ b/playwright-tests/web/src/main.rs @@ -1,4 +1,4 @@ -// This test is used by playwrite configured in the root of the repo +// This test is used by playwright configured in the root of the repo use dioxus::prelude::*; use dioxus_web::use_eval; diff --git a/playwright.config.js b/playwright.config.js index 5d2c3642f..74f1391d5 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -1,6 +1,6 @@ // @ts-check -const { defineConfig, devices } = require('@playwright/test'); -const path = require('path'); +const { defineConfig, devices } = require("@playwright/test"); +const path = require("path"); /** * Read environment variables from file. @@ -12,7 +12,7 @@ const path = require('path'); * @see https://playwright.dev/docs/test-configuration */ module.exports = defineConfig({ - testDir: './playwrite-tests', + testDir: "./playwright-tests", /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -22,21 +22,21 @@ module.exports = defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: "html", /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ // baseURL: 'http://127.0.0.1:3000', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: "on-first-retry", }, /* Configure projects for major browsers */ projects: [ { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + name: "chromium", + use: { ...devices["Desktop Chrome"] }, }, // { @@ -73,27 +73,29 @@ module.exports = defineConfig({ /* Run your local dev server before starting the tests */ webServer: [ { - command: 'cargo run --package dioxus-playwrite-liveview-test --bin dioxus-playwrite-liveview-test', + command: + "cargo run --package dioxus-playwright-liveview-test --bin dioxus-playwright-liveview-test", port: 3030, timeout: 10 * 60 * 1000, reuseExistingServer: !process.env.CI, stdout: "pipe", }, { - cwd: path.join(process.cwd(), 'playwrite-tests', 'web'), - command: 'dioxus serve', + cwd: path.join(process.cwd(), "playwright-tests", "web"), + command: "dioxus serve", port: 8080, timeout: 10 * 60 * 1000, reuseExistingServer: !process.env.CI, stdout: "pipe", }, { - cwd: path.join(process.cwd(), 'playwrite-tests', 'fullstack'), - command: 'dioxus build --features web\ncargo run --release --features ssr --no-default-features', + cwd: path.join(process.cwd(), "playwright-tests", "fullstack"), + command: + "dioxus build --features web\ncargo run --release --features ssr --no-default-features", port: 3333, timeout: 10 * 60 * 1000, reuseExistingServer: !process.env.CI, stdout: "pipe", - } + }, ], }); From 235406b59d3ee3e0f302e1ad80bde21e7eb19e9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 11:01:13 -0500 Subject: [PATCH 19/20] Bump JamesIves/github-pages-deploy-action from 4.4.2 to 4.4.3 (#1205) Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.4.2 to 4.4.3. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.4.2...v4.4.3) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs stable.yml | 2 +- .github/workflows/docs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs stable.yml b/.github/workflows/docs stable.yml index 98245fa89..a2d4e2c07 100644 --- a/.github/workflows/docs stable.yml +++ b/.github/workflows/docs stable.yml @@ -29,7 +29,7 @@ jobs: # cd fermi && mdbook build -d ../nightly/fermi && cd .. - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4.4.2 + uses: JamesIves/github-pages-deploy-action@v4.4.3 with: branch: gh-pages # The branch the action should deploy to. folder: docs/nightly # The folder the action should deploy. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d209c0c1a..dafe0384d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: # cd fermi && mdbook build -d ../nightly/fermi && cd .. - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4.4.2 + uses: JamesIves/github-pages-deploy-action@v4.4.3 with: branch: gh-pages # The branch the action should deploy to. folder: docs/nightly # The folder the action should deploy. From 9cbc086aa186e2b12d9fa068173970d18af3ba63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Esp=C3=ADn?= Date: Mon, 17 Jul 2023 18:02:02 +0200 Subject: [PATCH 20/20] fix: Update use_ref docs (#1201) --- packages/hooks/src/useref.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/hooks/src/useref.rs b/packages/hooks/src/useref.rs index 1865dadb3..cf1b0306f 100644 --- a/packages/hooks/src/useref.rs +++ b/packages/hooks/src/useref.rs @@ -16,7 +16,7 @@ use std::{ /// writes through the `write` method. Whenever `write` is called, the component /// that initialized the hook will be marked as "dirty". /// -/// ```rust, ignore +/// ```rust, no_run /// let val = use_ref(|| HashMap::::new()); /// /// // using `write` will give us a `RefMut` to the inner value, which we can call methods on @@ -26,7 +26,7 @@ use std::{ /// /// You can avoid this default behavior with `write_silent` /// -/// ```ignore +/// ```rust, no_run /// // with `write_silent`, the component will not be re-rendered /// val.write_silent().insert(2, "goodbye".to_string()); /// ``` @@ -35,7 +35,7 @@ use std::{ /// /// To read values out of the refcell, you can use the `read` method which will retrun a `Ref`. /// -/// ```rust, ignore +/// ```rust, no_run /// let map: Ref<_> = val.read(); /// /// let item = map.get(&1); @@ -43,7 +43,7 @@ use std::{ /// /// To get an &T out of the RefCell, you need to "reborrow" through the Ref: /// -/// ```rust, ignore +/// ```rust, no_run /// let read = val.read(); /// let map = &*read; /// ``` @@ -54,10 +54,10 @@ use std::{ /// Typically this will be a collection like a HashMap or a Vec. To create new /// elements from the collection, we can use `read()` directly in our rsx!. /// -/// ```rust, ignore +/// ```rust, no_run /// rsx!{ /// val.read().iter().map(|(k, v)| { -/// rsx!{ key: "{k}", value: "{v}" } +/// rsx!{ key: "{k}", "{v}" } /// }) /// } /// ``` @@ -66,9 +66,9 @@ use std::{ /// "render" inside the iterator. For some cases you might need to collect into /// a temporary Vec. /// -/// ```rust, ignore +/// ```rust, no_run /// let items = val.read().iter().map(|(k, v)| { -/// cx.render(rsx!{ key: "{k}", value: "{v}" }) +/// cx.render(rsx!{ key: "{k}", "{v}" }) /// }); /// /// // collect into a Vec @@ -80,9 +80,9 @@ use std::{ /// /// To access values from a `UseRef` in an async context, you need to detach it /// from the current scope's lifetime, making it a `'static` value. This is done -/// by simply calling `ToOnwed` or `Clone`. +/// by simply calling `to_owned` or `clone`. /// -/// ```rust, ignore +/// ```rust, no_run /// let val = use_ref(|| HashMap::::new()); /// /// cx.spawn({ @@ -95,15 +95,15 @@ use std::{ /// ``` /// /// If you're working with lots of values like UseState and UseRef, you can use the -/// `clone!` macro to make it easier to write the above code. +/// `to_owned!` macro to make it easier to write the above code. /// -/// ```rust, ignore +/// ```rust, no_run /// let val1 = use_ref(|| HashMap::::new()); /// let val2 = use_ref(|| HashMap::::new()); /// let val3 = use_ref(|| HashMap::::new()); /// /// cx.spawn({ -/// clone![val1, val2, val3]; +/// to_owned![val1, val2, val3]; /// async move { /// some_work().await; /// val.write().insert(1, "hello".to_string()); @@ -194,7 +194,7 @@ impl UseRef { /// Note: You can always "reborrow" the value through the RefCell. /// This method just does it for you automatically. /// - /// ```rust, ignore + /// ```rust, no_run /// let val = use_ref(|| HashMap::::new()); /// /// @@ -214,7 +214,7 @@ impl UseRef { /// Note: You can always "reborrow" the value through the RefCell. /// This method just does it for you automatically. /// - /// ```rust, ignore + /// ```rust, no_run /// let val = use_ref(|| HashMap::::new()); /// ///