diff --git a/examples/callback.rs b/examples/callback.rs deleted file mode 100644 index 2975d8305..000000000 --- a/examples/callback.rs +++ /dev/null @@ -1,22 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - dioxus_desktop::launch(app); -} - -fn app() -> Element { - let onclick = use_callback!(move |_| async move { - let res = reqwest::get("https://dog.ceo/api/breeds/list/all") - .await - .unwrap() - .text() - .await - .unwrap(); - - println!("{res:#?}, "); - }); - - rsx! { - button { onclick, "Click me!" } - } -} diff --git a/examples/crm.rs b/examples/crm.rs index 0ab034a02..0d3d36fa7 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -97,7 +97,7 @@ fn ClientAdd() -> Element { label { "for": "first_name", "First Name" } input { id: "first_name", - "type": "text", + r#type: "text", placeholder: "First Name…", required: "", value: "{first_name}", @@ -109,7 +109,7 @@ fn ClientAdd() -> Element { label { "for": "last_name", "Last Name" } input { id: "last_name", - "type": "text", + r#type: "text", placeholder: "Last Name…", required: "", value: "{last_name}", @@ -128,7 +128,7 @@ fn ClientAdd() -> Element { } div { class: "pure-controls", - button { "type": "submit", class: "pure-button pure-button-primary", "Save" } + button { r#type: "submit", class: "pure-button pure-button-primary", "Save" } Link { to: Route::ClientList {}, class: "pure-button pure-button-primary red", "Cancel" } } } diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 91ff0f36d..5f0f90eb9 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -29,10 +29,7 @@ fn app() -> Element { ul { flex: "50%", for cur_breed in breed_list.message.keys().take(10) { li { key: "{cur_breed}", - button { - onclick: move |_| breed.set(cur_breed.clone()), - "{cur_breed}" - } + button { onclick: move |_| breed.set(cur_breed.clone()), "{cur_breed}" } } } } diff --git a/packages/hooks/src/use_callback.rs b/packages/hooks/src/use_callback.rs deleted file mode 100644 index edb5c1bb1..000000000 --- a/packages/hooks/src/use_callback.rs +++ /dev/null @@ -1,48 +0,0 @@ -use dioxus_core::ScopeState; -use std::future::Future; - -#[macro_export] -macro_rules! use_callback { - // ($cx:ident, || || $($rest:tt)*) => { use_callback( $cx, (), |_| $($rest)* ) }; - // ($cx:ident, || || $($rest:tt)*) => { use_callback( $cx, (), |_| $($rest)* ) }; - ($cx:ident, || $($rest:tt)*) => { - use_callback( - $cx, - move || $($rest)* - ) - }; - ($cx:ident, |$($args:tt),* | $($rest:tt)*) => { - use_callback( - $cx, - move || $($rest)* - ) - }; - ($cx:ident, $($rest:tt)*) => { - use_callback( - $cx, - move || $($rest)* - ) - }; -} - -pub fn use_callback(, make: impl FnOnce() -> R) -> impl FnMut(T) + '_ -where - R: FnMut(T) -> F + 'static, - F: Future + 'static, -{ - let mut hook = make(); - - move |evt| cx.spawn(hook(evt)) -} - -fn _it_works() { - let _p = use_callback(|| { - |()| async { - // - } - }); - - // let p = use_callback!(|| |evt| async { - // // - // }); -} diff --git a/packages/hooks/src/use_const.rs b/packages/hooks/src/use_const.rs deleted file mode 100644 index f3c6cfb03..000000000 --- a/packages/hooks/src/use_const.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::rc::Rc; - -use dioxus_core::prelude::*; - -/// Store constant state between component renders. -/// -/// UseConst allows you to store state that is initialized once and then remains constant across renders. -/// You can only get an immutable reference after initalization. -/// This can be useful for values that don't need to update reactively, thus can be memoized easily -/// -/// ```rust, ignore -/// struct ComplexData(i32); -/// -/// fn Component() -> Element { -/// let id = use_const(|| ComplexData(100)); -/// -/// rsx! { -/// div { "{id.0}" } -/// }) -/// } -/// ``` -#[must_use] -pub fn use_const - initial_state_fn: impl FnOnce() -> T, -) -> &UseConst { - cx.use_hook(|| UseConst { - value: Rc::new(initial_state_fn()), - }) -} - -#[derive(Clone)] -pub struct UseConst { - value: Rc, -} - -impl PartialEq for UseConst { - fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.value, &other.value) - } -} - -impl core::fmt::Display for UseConst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.value.fmt(f) - } -} - -impl UseConst { - pub fn get_rc(&self) -> &Rc { - &self.value - } -} - -impl std::ops::Deref for UseConst { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.value.as_ref() - } -} - -#[test] -fn use_const_makes_sense() { - #[allow(unused)] - - fn app() -> Element { - let const_val = use_const(|| vec![0, 1, 2, 3]); - - assert!(const_val[0] == 0); - - // const_val.remove(0); // Cannot Compile, cannot get mutable reference now - - None - } -} diff --git a/packages/hooks/src/use_effect.rs b/packages/hooks/src/use_effect.rs deleted file mode 100644 index b983fa320..000000000 --- a/packages/hooks/src/use_effect.rs +++ /dev/null @@ -1,174 +0,0 @@ -use dioxus_core::{ScopeState, Task}; -use std::{ - any::Any, - cell::{Cell, RefCell}, - future::Future, - rc::Rc, -}; - -use crate::UseFutureDep; - -/// A hook that provides a future that executes after the hooks have been applied. -/// -/// Whenever the hooks dependencies change, the future will be re-evaluated. -/// If a future is pending when the dependencies change, the previous future -/// will be allowed to continue. -/// -/// **Note:** If your dependency list is always empty, use [`use_on_create`](crate::use_on_create). -/// -/// ## Arguments -/// -/// - `dependencies`: a tuple of references to values that are `PartialEq` + `Clone`. -/// - `future`: a closure that takes the `dependencies` as arguments and returns a `'static` future. That future may return nothing or a closure that will be executed when the dependencies change to clean up the effect. -/// -/// ## Examples -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// #[component] -/// fn Profile(id: usize) -> Element { -/// let name = use_signal(|| None); -/// -/// // Only fetch the user data when the id changes. -/// use_effect((id,), |(id,)| { -/// to_owned![name]; -/// async move { -/// let user = fetch_user(id).await; -/// name.set(user.name); -/// } -/// }); -/// -/// // Only fetch the user data when the id changes. -/// use_effect((id,), |(id,)| { -/// to_owned![name]; -/// async move { -/// let user = fetch_user(id).await; -/// name.set(user.name); -/// move || println!("Cleaning up from {}", id) -/// } -/// }); -/// -/// let name = name.get().clone().unwrap_or("Loading...".to_string()); -/// -/// render!( -/// p { "{name}" } -/// ) -/// } -/// -/// #[component] -/// fn App() -> Element { -/// render!(Profile { id: 0 }) -/// } -/// ``` -pub fn use_effect(, dependencies: D, future: impl FnOnce(D::Out) -> R) -where - D: UseFutureDep, - R: UseEffectReturn, -{ - struct UseEffect { - needs_regen: bool, - task: Cell>, - dependencies: Vec>, - cleanup: UseEffectCleanup, - } - - impl Drop for UseEffect { - fn drop(&mut self) { - if let Some(cleanup) = self.cleanup.borrow_mut().take() { - cleanup(); - } - } - } - - let state = cx.use_hook(move || UseEffect { - needs_regen: true, - task: Cell::new(None), - dependencies: Vec::new(), - cleanup: Rc::new(RefCell::new(None)), - }); - - if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen { - // Call the cleanup function if it exists - if let Some(cleanup) = state.cleanup.borrow_mut().take() { - cleanup(); - } - - // We don't need regen anymore - state.needs_regen = false; - - // Create the new future - let return_value = future(dependencies.out()); - - let task = return_value.apply(state.cleanup.clone(), cx); - state.task.set(Some(task)); - } -} - -type UseEffectCleanup = Rc>>>; - -/// Something that can be returned from a `use_effect` hook. -pub trait UseEffectReturn { - fn apply(self, oncleanup: UseEffectCleanup, ) -> Task; -} - -impl UseEffectReturn<()> for T -where - T: Future + 'static, -{ - fn apply(self, _: UseEffectCleanup, ) -> Task { - cx.push_future(self) - } -} - -#[doc(hidden)] -pub struct CleanupFutureMarker; -impl UseEffectReturn for T -where - T: Future + 'static, - F: FnOnce() + 'static, -{ - fn apply(self, oncleanup: UseEffectCleanup, ) -> Task { - cx.push_future(async move { - let cleanup = self.await; - *oncleanup.borrow_mut() = Some(Box::new(cleanup) as Box); - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(unused)] - #[test] - fn test_use_future() { - use dioxus_core::prelude::*; - - struct MyProps { - a: String, - b: i32, - c: i32, - d: i32, - e: i32, - } - - fn app(cx: Scope) -> Element { - // should only ever run once - use_effect((), |_| async move { - // - }); - - // runs when a is changed - use_effect((&cx.props.a,), |(a,)| async move { - // - }); - - // runs when a or b is changed - use_effect((&cx.props.a, &cx.props.b), |(a, b)| async move { - // - }); - - todo!() - } - } -} diff --git a/packages/hooks/src/use_model.rs b/packages/hooks/src/use_model.rs deleted file mode 100644 index 0e81fe3f8..000000000 --- a/packages/hooks/src/use_model.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! When building complex components, it's occasionally useful to dip into a pure MVC pattern instead of the -//! React hooks pattern. Hooks are useful to abstract over some reusable logic, but many models are not reusable -//! in the same way that hooks are. -//! -//! In these cases, we provide `use_model` - a convenient way of abstracting over some state and async functions. - -use dioxus_core::prelude::ScopeState; -use std::{ - cell::{Cell, Ref, RefCell, RefMut}, - future::Future, - marker::PhantomData, - pin::Pin, - rc::Rc, -}; - -pub fn use_model<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) -> UseModel<'a, T> { - let inner = cx.use_hook(|| UseModelInner { - update_scheduled: Cell::new(false), - update_callback: cx.schedule_update(), - value: RefCell::new(f()), - // tasks: RefCell::new(Vec::new()), - }); - - inner.update_scheduled.set(false); - UseModel { inner } -} - -pub struct UseModel<'a, T> { - inner: &'a UseModelInner, -} - -struct UseModelInner { - update_scheduled: Cell, - update_callback: Rc, - value: RefCell, - // tasks: RefCell>, -} - -type ModelTask = Pin + 'static>>; - -impl<'a, T: 'static> UseModel<'a, T> { - pub fn read(&self) -> Ref<'_, T> { - self.inner.value.borrow() - } - pub fn write(&self) -> RefMut<'_, T> { - self.needs_update(); - self.inner.value.borrow_mut() - } - /// Allows the ability to write the value without forcing a re-render - pub fn write_silent(&self) -> RefMut<'_, T> { - self.inner.value.borrow_mut() - } - - pub fn needs_update(&self) { - if !self.inner.update_scheduled.get() { - self.inner.update_scheduled.set(true); - (self.inner.update_callback)(); - } - } - - pub fn set(&self, new: T) { - *self.inner.value.borrow_mut() = new; - self.needs_update(); - } - - pub fn read_write(&self) -> (Ref<'_, T>, &Self) { - (self.read(), self) - } - - pub fn start(&self, _f: impl FnOnce() -> ModelTask) { - todo!() - } -} - -// keep a coroutine going -pub fn use_model_coroutine<'a, T, F: Future + 'static>( - cx: &'a ScopeState, - _model: UseModel, - _f: impl FnOnce(AppModels) -> F, -) -> UseModelCoroutine { - cx.use_hook(|| UseModelTaskInner { - task: Default::default(), - }); - todo!() -} - -impl Copy for UseModel<'_, T> {} -impl<'a, T> Clone for UseModel<'a, T> { - fn clone(&self) -> Self { - Self { inner: self.inner } - } -} diff --git a/packages/hooks/src/use_ref.rs b/packages/hooks/src/use_ref.rs deleted file mode 100644 index 97659860e..000000000 --- a/packages/hooks/src/use_ref.rs +++ /dev/null @@ -1,252 +0,0 @@ -use dioxus_core::ScopeState; -use std::{ - cell::{Cell, Ref, RefCell, RefMut}, - rc::Rc, - sync::Arc, -}; - -/// `use_ref` is a key foundational hook for storing state in Dioxus. -/// -/// It is different that `use_state` in that the value stored is not "immutable". -/// Instead, UseRef is designed to store larger values that will be mutated at will. -/// -/// ## Writing Values -/// -/// Generally, `use_ref` is just a wrapper around a RefCell that tracks mutable -/// writes through the `write` method. Whenever `write` is called, the component -/// that initialized the hook will be marked as "dirty". -/// -/// ```rust, no_run -/// let val = use_signal(|| HashMap::::new()); -/// -/// // using `write` will give us a `RefMut` to the inner value, which we can call methods on -/// // This marks the component as "dirty" -/// val.write().insert(1, "hello".to_string()); -/// ``` -/// -/// You can avoid this default behavior with `write_silent` -/// -/// ```rust, no_run -/// // with `write_silent`, the component will not be re-rendered -/// val.write_silent().insert(2, "goodbye".to_string()); -/// ``` -/// -/// ## Reading Values -/// -/// To read values out of the refcell, you can use the `read` method which will retrun a `Ref`. -/// -/// ```rust, no_run -/// let map: Ref<_> = val.read(); -/// -/// let item = map.get(&1); -/// ``` -/// -/// To get an &T out of the RefCell, you need to "reborrow" through the Ref: -/// -/// ```rust, no_run -/// let read = val.read(); -/// let map = &*read; -/// ``` -/// -/// ## Collections and iteration -/// -/// A common usecase for `use_ref` is to store a large amount of data in a component. -/// 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, no_run -/// rsx!{ -/// val.read().iter().map(|(k, v)| { -/// rsx!{ key: "{k}", "{v}" } -/// }) -/// } -/// ``` -/// -/// If you are generating elements outside of `rsx!` then you might need to call -/// "render" inside the iterator. For some cases you might need to collect into -/// a temporary Vec. -/// -/// ```rust, no_run -/// let items = val.read().iter().map(|(k, v)| { -/// rsx!{ key: "{k}", "{v}" }) -/// }); -/// -/// // collect into a Vec -/// -/// let items: Vec = items.collect(); -/// ``` -/// -/// ## Use in Async -/// -/// 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 `to_owned` or `clone`. -/// -/// ```rust, no_run -/// let val = use_signal(|| HashMap::::new()); -/// -/// cx.spawn({ -/// let val = val.clone(); -/// async move { -/// some_work().await; -/// val.write().insert(1, "hello".to_string()); -/// } -/// }) -/// ``` -/// -/// If you're working with lots of values like UseState and UseRef, you can use the -/// `to_owned!` macro to make it easier to write the above code. -/// -/// ```rust, no_run -/// let val1 = use_signal(|| HashMap::::new()); -/// let val2 = use_signal(|| HashMap::::new()); -/// let val3 = use_signal(|| HashMap::::new()); -/// -/// cx.spawn({ -/// to_owned![val1, val2, val3]; -/// async move { -/// some_work().await; -/// val.write().insert(1, "hello".to_string()); -/// } -/// }) -/// ``` -#[must_use] -pub fn use_ref(nitialize_refcell: impl FnOnce() -> T) -> Signal { - let hook = cx.use_hook(|| UseRef { - update: cx.schedule_update(), - value: Rc::new(RefCell::new(initialize_refcell())), - dirty: Rc::new(Cell::new(false)), - gen: 0, - }); - - if hook.dirty.get() { - hook.gen += 1; - hook.dirty.set(false); - } - - hook -} - -/// A type created by the [`use_ref`] hook. See its documentation for more details. -pub struct UseRef { - update: Arc, - value: Rc>, - dirty: Rc>, - gen: usize, -} - -impl Clone for UseRef { - fn clone(&self) -> Self { - Self { - update: self.update.clone(), - value: self.value.clone(), - dirty: self.dirty.clone(), - gen: self.gen, - } - } -} - -impl UseRef { - /// Read the value in the RefCell into a `Ref`. If this method is called - /// while other values are still being `read` or `write`, then your app will crash. - /// - /// Be very careful when working with this method. If you can, consider using - /// the `with` and `with_mut` methods instead, choosing to render Elements - /// during the read calls. - pub fn read(&self) -> Ref<'_, T> { - self.value.borrow() - } - - /// Mutably unlock the value in the RefCell. This will mark the component as "dirty" - /// - /// Uses to `write` should be as short as possible. - /// - /// Be very careful when working with this method. If you can, consider using - /// the `with` and `with_mut` methods instead, choosing to render Elements - /// during the read and write calls. - pub fn write(&self) -> RefMut<'_, T> { - self.needs_update(); - self.value.borrow_mut() - } - - /// Set the curernt value to `new_value`. This will mark the component as "dirty" - /// - /// This change will propagate immediately, so any other contexts that are - /// using this RefCell will also be affected. If called during an async context, - /// the component will not be re-rendered until the next `.await` call. - pub fn set(&self, new: T) { - *self.value.borrow_mut() = new; - self.needs_update(); - } - - /// Mutably unlock the value in the RefCell. This will not mark the component as dirty. - /// This is useful if you want to do some work without causing the component to re-render. - /// - /// Uses to `write` should be as short as possible. - /// - /// Be very careful when working with this method. If you can, consider using - /// the `with` and `with_mut` methods instead, choosing to render Elements - pub fn write_silent(&self) -> RefMut<'_, T> { - self.value.borrow_mut() - } - - /// Take a reference to the inner value termporarily and produce a new value - /// - /// Note: You can always "reborrow" the value through the RefCell. - /// This method just does it for you automatically. - /// - /// ```rust, no_run - /// let val = use_signal(|| HashMap::::new()); - /// - /// - /// // use reborrowing - /// let inner = &*val.read(); - /// - /// // or, be safer and use `with` - /// val.with(|i| println!("{:?}", i)); - /// ``` - pub fn with(&self, immutable_callback: impl FnOnce(&T) -> O) -> O { - immutable_callback(&*self.read()) - } - - /// Take a reference to the inner value termporarily and produce a new value, - /// modifying the original in place. - /// - /// Note: You can always "reborrow" the value through the RefCell. - /// This method just does it for you automatically. - /// - /// ```rust, no_run - /// let val = use_signal(|| HashMap::::new()); - /// - /// - /// // use reborrowing - /// let inner = &mut *val.write(); - /// - /// // or, be safer and use `with` - /// val.with_mut(|i| i.insert(1, "hi")); - /// ``` - pub fn with_mut(&self, mutable_callback: impl FnOnce(&mut T) -> O) -> O { - mutable_callback(&mut *self.write()) - } - - /// Call the inner callback to mark the originator component as dirty. - /// - /// This will cause the component to be re-rendered after the current scope - /// has ended or the current async task has been yielded through await. - pub fn needs_update(&self) { - self.dirty.set(true); - (self.update)(); - } -} - -// UseRef memoizes not on value but on cell -// Memoizes on "generation" - so it will cause a re-render if the value changes -impl PartialEq for UseRef { - fn eq(&self, other: &Self) -> bool { - if Rc::ptr_eq(&self.value, &other.value) { - self.gen == other.gen - } else { - false - } - } -} diff --git a/packages/hooks/src/use_signal.rs b/packages/hooks/src/use_signal.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/hooks/src/use_state.rs b/packages/hooks/src/use_state.rs deleted file mode 100644 index 6e01df988..000000000 --- a/packages/hooks/src/use_state.rs +++ /dev/null @@ -1,524 +0,0 @@ -#![warn(clippy::pedantic)] - -use dioxus_core::prelude::*; -use std::{ - cell::{RefCell, RefMut}, - fmt::{Debug, Display}, - ops::{Add, Div, Mul, Not, Sub}, - rc::Rc, - sync::Arc, -}; - -/// Store state between component renders. -/// -/// ## Dioxus equivalent of useState, designed for Rust -/// -/// The Dioxus version of `useState` for state management inside components. It allows you to ergonomically store and -/// modify state between component renders. When the state is updated, the component will re-render. -/// -/// -/// ```ignore -/// const Example: Component = |cx| { -/// let count = use_signal(|| 0); -/// -/// rsx! { -/// div { -/// h1 { "Count: {count}" } -/// button { onclick: move |_| *count.modify() += 1, "Increment" } -/// button { onclick: move |_| *count.modify() -= 1, "Decrement" } -/// } -/// )) -/// } -/// ``` -#[must_use] -pub fn use_state - initial_state_fn: impl FnOnce() -> T, -) -> &UseState { - let hook = cx.use_hook(move || { - let current_val = Rc::new(initial_state_fn()); - let update_callback = cx.schedule_update(); - let slot = Rc::new(RefCell::new(current_val.clone())); - let setter = Rc::new({ - to_owned![update_callback, slot]; - move |new| { - { - let mut slot = slot.borrow_mut(); - - // if there's only one reference (weak or otherwise), we can just swap the values - // Typically happens when the state is set multiple times - we don't want to create a new Rc for each new value - if let Some(val) = Rc::get_mut(&mut slot) { - *val = new; - } else { - *slot = Rc::new(new); - } - } - update_callback(); - } - }); - - UseState { - current_val, - update_callback, - setter, - slot, - } - }); - - hook.current_val = hook.slot.borrow().clone(); - - hook -} - -pub struct UseState { - pub(crate) current_val: Rc, - pub(crate) update_callback: Arc, - pub(crate) setter: Rc, - pub(crate) slot: Rc>>, -} - -impl UseState { - /// Set the state to a new value. - pub fn set(&self, new: T) { - (self.setter)(new); - } - - /// Get the current value of the state by cloning its container Rc. - /// - /// This is useful when you are dealing with state in async contexts but need - /// to know the current value. You are not given a reference to the state. - /// - /// # Examples - /// An async context might need to know the current value: - /// - /// ```rust, ignore - /// fn component() -> Element { - /// let count = use_signal(|| 0); - /// cx.spawn({ - /// let set_count = count.to_owned(); - /// async move { - /// let current = set_count.current(); - /// } - /// }) - /// } - /// ``` - #[must_use] - pub fn current(&self) -> Rc { - self.slot.borrow().clone() - } - - /// Get the `setter` function directly without the `UseState` wrapper. - /// - /// This is useful for passing the setter function to other components. - /// - /// However, for most cases, calling `to_owned` on the state is the - /// preferred way to get "another" state handle. - /// - /// - /// # Examples - /// A component might require an `Rc` as an input to set a value. - /// - /// ```rust, ignore - /// fn component() -> Element { - /// let value = use_signal(|| 0); - /// - /// rsx!{ - /// Component { - /// handler: value.setter() - /// } - /// } - /// } - /// ``` - #[must_use] - pub fn setter(&self) -> Rc { - self.setter.clone() - } - - /// Set the state to a new value, using the current state value as a reference. - /// - /// This is similar to passing a closure to React's `set_value` function. - /// - /// # Examples - /// - /// Basic usage: - /// ```rust, ignore - /// # use dioxus_core::prelude::*; - /// # use dioxus_hooks::*; - /// fn component() -> Element { - /// let value = use_signal(|| 0); - /// - /// // to increment the value - /// value.modify(|v| v + 1); - /// - /// // usage in async - /// cx.spawn({ - /// let value = value.to_owned(); - /// async move { - /// value.modify(|v| v + 1); - /// } - /// }); - /// - /// # todo!() - /// } - /// ``` - pub fn modify(&self, f: impl FnOnce(&T) -> T) { - let new_val = { - let current = self.slot.borrow(); - f(current.as_ref()) - }; - (self.setter)(new_val); - } - - /// Get the value of the state when this handle was created. - /// - /// This method is useful when you want an `Rc` around the data to cheaply - /// pass it around your app. - /// - /// ## Warning - /// - /// This will return a stale value if used within async contexts. - /// - /// Try `current` to get the real current value of the state. - /// - /// ## Example - /// - /// ```rust, ignore - /// # use dioxus_core::prelude::*; - /// # use dioxus_hooks::*; - /// fn component() -> Element { - /// let value = use_signal(|| 0); - /// - /// let as_rc = value.get(); - /// assert_eq!(as_rc.as_ref(), &0); - /// - /// # todo!() - /// } - /// ``` - #[must_use] - pub fn get(&self) -> &T { - &self.current_val - } - - #[must_use] - pub fn get_rc(&self) -> &Rc { - &self.current_val - } - - /// Mark the component that create this [`UseState`] as dirty, forcing it to re-render. - /// - /// ```rust, ignore - /// fn component() -> Element { - /// let count = use_signal(|| 0); - /// cx.spawn({ - /// let count = count.to_owned(); - /// async move { - /// // for the component to re-render - /// count.needs_update(); - /// } - /// }) - /// } - /// ``` - pub fn needs_update(&self) { - (self.update_callback)(); - } -} - -impl UseState { - /// Get a mutable handle to the value by calling `ToOwned::to_owned` on the - /// current value. - /// - /// This is essentially cloning the underlying value and then setting it, - /// giving you a mutable handle in the process. This method is intended for - /// types that are cheaply cloneable. - /// - /// If you are comfortable dealing with `RefMut`, then you can use `make_mut` to get - /// the underlying slot. However, be careful with `RefMut` since you might panic - /// if the `RefCell` is left open. - /// - /// # Examples - /// - /// ```rust, ignore - /// let val = use_signal(|| 0); - /// - /// val.with_mut(|v| *v = 1); - /// ``` - pub fn with_mut(&self, apply: impl FnOnce(&mut T)) { - let mut slot = self.slot.borrow_mut(); - let mut inner = slot.as_ref().to_owned(); - - apply(&mut inner); - - if let Some(new) = Rc::get_mut(&mut slot) { - *new = inner; - } else { - *slot = Rc::new(inner); - } - - self.needs_update(); - } - - /// Get a mutable handle to the value by calling `ToOwned::to_owned` on the - /// current value. - /// - /// This is essentially cloning the underlying value and then setting it, - /// giving you a mutable handle in the process. This method is intended for - /// types that are cheaply cloneable. - /// - /// # Warning - /// Be careful with `RefMut` since you might panic if the `RefCell` is left open! - /// - /// # Examples - /// - /// ```rust, ignore - /// let val = use_signal(|| 0); - /// - /// *val.make_mut() += 1; - /// ``` - #[must_use] - #[allow(clippy::missing_panics_doc)] - pub fn make_mut(&self) -> RefMut { - let mut slot = self.slot.borrow_mut(); - - self.needs_update(); - - if Rc::strong_count(&*slot) > 0 { - *slot = Rc::new(slot.as_ref().to_owned()); - } - - RefMut::map(slot, |rc| Rc::get_mut(rc).expect("the hard count to be 0")) - } - - /// Convert this handle to a tuple of the value and the handle itself. - #[must_use] - pub fn split(&self) -> (&T, &Self) { - (&self.current_val, self) - } -} - -impl Clone for UseState { - fn clone(&self) -> Self { - UseState { - current_val: self.current_val.clone(), - update_callback: self.update_callback.clone(), - setter: self.setter.clone(), - slot: self.slot.clone(), - } - } -} - -impl std::fmt::Display for UseState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.current_val) - } -} - -impl std::fmt::Binary for UseState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:b}", self.current_val.as_ref()) - } -} - -impl PartialEq for UseState { - fn eq(&self, other: &T) -> bool { - self.current_val.as_ref() == other - } -} - -// todo: this but for more interesting conrete types -impl PartialEq for &UseState { - fn eq(&self, other: &bool) -> bool { - self.current_val.as_ref() == other - } -} - -impl PartialEq> for UseState { - fn eq(&self, other: &UseState) -> bool { - Rc::ptr_eq(&self.current_val, &other.current_val) - } -} - -impl PartialOrd for UseState { - fn ge(&self, other: &T) -> bool { - *self.current_val >= *other - } - - fn gt(&self, other: &T) -> bool { - *self.current_val > *other - } - - fn le(&self, other: &T) -> bool { - *self.current_val <= *other - } - - fn lt(&self, other: &T) -> bool { - *self.current_val < *other - } - - fn partial_cmp(&self, other: &T) -> Option { - (*self.current_val).partial_cmp(other) - } -} - -impl PartialOrd> for UseState { - fn ge(&self, other: &UseState) -> bool { - self.current_val >= other.current_val - } - - fn gt(&self, other: &UseState) -> bool { - self.current_val > other.current_val - } - - fn le(&self, other: &UseState) -> bool { - self.current_val <= other.current_val - } - - fn lt(&self, other: &UseState) -> bool { - self.current_val < other.current_val - } - - fn partial_cmp(&self, other: &UseState) -> Option { - self.current_val.partial_cmp(&other.current_val) - } -} - -impl Debug for UseState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.current_val) - } -} - -impl std::ops::Deref for UseState { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.current_val.as_ref() - } -} - -impl std::ops::Not for &UseState { - type Output = ::Output; - - fn not(self) -> Self::Output { - self.current_val.not() - } -} - -impl std::ops::Not for UseState { - type Output = ::Output; - - fn not(self) -> Self::Output { - self.current_val.not() - } -} - -impl std::ops::Add for &UseState { - type Output = ::Output; - - fn add(self, other: T) -> Self::Output { - *self.current_val.as_ref() + other - } -} -impl std::ops::Sub for &UseState { - type Output = ::Output; - - fn sub(self, other: T) -> Self::Output { - *self.current_val.as_ref() - other - } -} - -impl std::ops::Div for &UseState { - type Output = ::Output; - - fn div(self, other: T) -> Self::Output { - *self.current_val.as_ref() / other - } -} - -impl std::ops::Mul for &UseState { - type Output = ::Output; - - fn mul(self, other: T) -> Self::Output { - *self.current_val.as_ref() * other - } -} - -impl + Copy> std::ops::AddAssign for &UseState { - fn add_assign(&mut self, rhs: T) { - self.set((*self.current()) + rhs); - } -} - -impl + Copy> std::ops::SubAssign for &UseState { - fn sub_assign(&mut self, rhs: T) { - self.set((*self.current()) - rhs); - } -} - -impl + Copy> std::ops::MulAssign for &UseState { - fn mul_assign(&mut self, rhs: T) { - self.set((*self.current()) * rhs); - } -} - -impl + Copy> std::ops::DivAssign for &UseState { - fn div_assign(&mut self, rhs: T) { - self.set((*self.current()) / rhs); - } -} - -impl + Copy> std::ops::AddAssign for UseState { - fn add_assign(&mut self, rhs: T) { - self.set((*self.current()) + rhs); - } -} - -impl + Copy> std::ops::SubAssign for UseState { - fn sub_assign(&mut self, rhs: T) { - self.set((*self.current()) - rhs); - } -} - -impl + Copy> std::ops::MulAssign for UseState { - fn mul_assign(&mut self, rhs: T) { - self.set((*self.current()) * rhs); - } -} - -impl + Copy> std::ops::DivAssign for UseState { - fn div_assign(&mut self, rhs: T) { - self.set((*self.current()) / rhs); - } -} - -#[test] -fn api_makes_sense() { - #[allow(unused)] - fn app() -> Element { - let val = use_signal(|| 0); - - val.set(0); - val.modify(|v| v + 1); - let real_current = val.current(); - - match val.get() { - 10 => { - val.set(20); - val.modify(|v| v + 1); - } - 20 => {} - _ => { - println!("{real_current}"); - } - } - - cx.spawn({ - to_owned![val]; - async move { - val.modify(|f| f + 1); - } - }); - - // cx.render(LazyNodes::new(|f| f.static_text("asd"))) - - todo!() - } -}