diff --git a/examples/calculator.rs b/examples/calculator.rs index 67cf40a5c..f63b8a754 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -18,20 +18,16 @@ fn main() { } fn app(cx: Scope) -> Element { - let (display_value, set_display_value) = use_state(&cx, || String::from("0")); + let val = use_state(&cx, || String::from("0")); let input_digit = move |num: u8| { - if display_value == "0" { - set_display_value(String::new()); + if val.get() == "0" { + val.set(String::new()); } - set_display_value - .make_mut() - .push_str(num.to_string().as_str()); + val.make_mut().push_str(num.to_string().as_str()); }; - let input_operator = move |key: &str| { - set_display_value.make_mut().push_str(key); - }; + let input_operator = move |key: &str| val.make_mut().push_str(key); cx.render(rsx!( style { [include_str!("./assets/calculator.css")] } @@ -54,34 +50,34 @@ fn app(cx: Scope) -> Element { KeyCode::Num8 => input_digit(8), KeyCode::Num9 => input_digit(9), KeyCode::Backspace => { - if !display_value.len() != 0 { - set_display_value.make_mut().pop(); + if !val.len() != 0 { + val.make_mut().pop(); } } _ => {} }, - div { class: "calculator-display", [display_value.to_string()] } + div { class: "calculator-display", [val.to_string()] } div { class: "calculator-keypad", div { class: "input-keys", div { class: "function-keys", button { class: "calculator-key key-clear", onclick: move |_| { - set_display_value(String::new()); - if !display_value.is_empty(){ - set_display_value("0".into()); + val.set(String::new()); + if !val.is_empty(){ + val.set("0".into()); } }, - [if display_value.is_empty() { "C" } else { "AC" }] + [if val.is_empty() { "C" } else { "AC" }] } button { class: "calculator-key key-sign", onclick: move |_| { - let temp = calc_val(display_value.clone()); + let temp = calc_val(val.as_str()); if temp > 0.0 { - set_display_value(format!("-{}", temp)); + val.set(format!("-{}", temp)); } else { - set_display_value(format!("{}", temp.abs())); + val.set(format!("{}", temp.abs())); } }, "±" @@ -89,8 +85,8 @@ fn app(cx: Scope) -> Element { button { class: "calculator-key key-percent", onclick: move |_| { - set_display_value( - format!("{}", calc_val(display_value.clone()) / 100.0) + val.set( + format!("{}", calc_val(val.as_str()) / 100.0) ); }, "%" @@ -100,7 +96,7 @@ fn app(cx: Scope) -> Element { button { class: "calculator-key key-0", onclick: move |_| input_digit(0), "0" } - button { class: "calculator-key key-dot", onclick: move |_| set_display_value.make_mut().push('.'), + button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'), "●" } (1..10).map(|k| rsx!{ @@ -114,25 +110,21 @@ fn app(cx: Scope) -> Element { } } div { class: "operator-keys", - button { class: "calculator-key key-divide", - onclick: move |_| input_operator("/"), + button { class: "calculator-key key-divide", onclick: move |_| input_operator("/"), "÷" } - button { class: "calculator-key key-multiply", - onclick: move |_| input_operator("*"), + button { class: "calculator-key key-multiply", onclick: move |_| input_operator("*"), "×" } - button { class: "calculator-key key-subtract", - onclick: move |_| input_operator("-"), + button { class: "calculator-key key-subtract", onclick: move |_| input_operator("-"), "−" } - button { class: "calculator-key key-add", - onclick: move |_| input_operator("+"), + button { class: "calculator-key key-add", onclick: move |_| input_operator("+"), "+" } button { class: "calculator-key key-equals", onclick: move |_| { - set_display_value(format!("{}", calc_val(display_value.clone()))); + val.set(format!("{}", calc_val(val.as_str()))); }, "=" } @@ -145,7 +137,7 @@ fn app(cx: Scope) -> Element { )) } -fn calc_val(val: String) -> f64 { +fn calc_val(val: &str) -> f64 { let mut temp = String::new(); let mut operation = "+".to_string(); diff --git a/examples/crm.rs b/examples/crm.rs index f6e7e05af..7b183e25b 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -21,10 +21,10 @@ pub struct Client { fn app(cx: Scope) -> Element { let clients = use_ref(&cx, || vec![] as Vec); - let (scene, set_scene) = use_state(&cx, || Scene::ClientsList); - let (firstname, set_firstname) = use_state(&cx, String::new); - let (lastname, set_lastname) = use_state(&cx, String::new); - let (description, set_description) = use_state(&cx, String::new); + let scene = use_state(&cx, || Scene::ClientsList); + let firstname = use_state(&cx, String::new); + let lastname = use_state(&cx, String::new); + let description = use_state(&cx, String::new); cx.render(rsx!( body { @@ -38,7 +38,7 @@ fn app(cx: Scope) -> Element { h1 {"Dioxus CRM Example"} - match scene { + match scene.get() { Scene::ClientsList => rsx!( div { class: "crm", h2 { margin_bottom: "10px", "List of clients" } @@ -51,8 +51,8 @@ fn app(cx: Scope) -> Element { }) ) } - button { class: "pure-button pure-button-primary", onclick: move |_| set_scene(Scene::NewClientForm), "Add New" } - button { class: "pure-button", onclick: move |_| set_scene(Scene::Settings), "Settings" } + button { class: "pure-button pure-button-primary", onclick: move |_| scene.set(Scene::NewClientForm), "Add New" } + button { class: "pure-button", onclick: move |_| scene.set(Scene::Settings), "Settings" } } ), Scene::NewClientForm => rsx!( @@ -63,36 +63,36 @@ fn app(cx: Scope) -> Element { class: "new-client firstname", placeholder: "First name", value: "{firstname}", - oninput: move |e| set_firstname(e.value.clone()) + oninput: move |e| firstname.set(e.value.clone()) } input { class: "new-client lastname", placeholder: "Last name", value: "{lastname}", - oninput: move |e| set_lastname(e.value.clone()) + oninput: move |e| lastname.set(e.value.clone()) } textarea { class: "new-client description", placeholder: "Description", value: "{description}", - oninput: move |e| set_description(e.value.clone()) + oninput: move |e| description.set(e.value.clone()) } } button { class: "pure-button pure-button-primary", onclick: move |_| { clients.write().push(Client { - description: (*description).clone(), - first_name: (*firstname).clone(), - last_name: (*lastname).clone(), + description: (**description).clone(), + first_name: (**firstname).clone(), + last_name: (**lastname).clone(), }); - set_description(String::new()); - set_firstname(String::new()); - set_lastname(String::new()); + description.set(String::new()); + firstname.set(String::new()); + lastname.set(String::new()); }, "Add New" } - button { class: "pure-button", onclick: move |_| set_scene(Scene::ClientsList), + button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" } } @@ -108,7 +108,7 @@ fn app(cx: Scope) -> Element { } button { class: "pure-button pure-button-primary", - onclick: move |_| set_scene(Scene::ClientsList), + onclick: move |_| scene.set(Scene::ClientsList), "Go Back" } } diff --git a/examples/disabled.rs b/examples/disabled.rs index 949c25369..087c270bc 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -5,13 +5,15 @@ fn main() { } fn app(cx: Scope) -> Element { - let (disabled, set_disabled) = use_state(&cx, || false); + let disabled = use_state(&cx, || false); cx.render(rsx! { div { button { - onclick: move |_| set_disabled(!disabled), - "click to " [if *disabled {"enable"} else {"disable"} ] " the lower button" + onclick: move |_| disabled.set(!disabled), + "click to " + [if disabled == true {"enable"} else {"disable"}] + " the lower button" } button { diff --git a/examples/dog_app.rs b/examples/dog_app.rs index ad983035e..b2c491d58 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -16,7 +16,7 @@ struct ListBreeds { } fn app(cx: Scope) -> Element { - let (breed, set_breed) = use_state(&cx, || None); + let breed = use_state(&cx, || None); let breeds = use_future(&cx, (), |_| async move { reqwest::get("https://dog.ceo/api/breeds/list/all") @@ -32,17 +32,17 @@ fn app(cx: Scope) -> Element { h1 { "Select a dog breed!" } div { display: "flex", ul { flex: "50%", - breeds.message.keys().map(|breed| rsx!( + breeds.message.keys().map(|cur_breed| rsx!( li { button { - onclick: move |_| set_breed(Some(breed.clone())), - "{breed}" + onclick: move |_| breed.set(Some(cur_breed.clone())), + "{cur_breed}" } } )) } div { flex: "50%", - match breed { + match breed.get() { Some(breed) => rsx!( Breed { breed: breed.clone() } ), None => rsx!("No Breed selected"), } diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index 8dba1cb91..55cb1b853 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -33,7 +33,7 @@ impl Label { fn app(cx: Scope) -> Element { let items = use_ref(&cx, Vec::new); - let (selected, set_selected) = use_state(&cx, || None); + let selected = use_state(&cx, || None); cx.render(rsx! { div { class: "container", @@ -71,7 +71,7 @@ fn app(cx: Scope) -> Element { rsx!(tr { class: "{is_in_danger}", td { class:"col-md-1" } td { class:"col-md-1", "{item.key}" } - td { class:"col-md-1", onclick: move |_| set_selected(Some(id)), + td { class:"col-md-1", onclick: move |_| selected.set(Some(id)), a { class: "lbl", item.labels } } td { class: "col-md-1", diff --git a/examples/hydration.rs b/examples/hydration.rs index 83b7633df..acfb3a320 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -20,13 +20,13 @@ fn main() { } fn app(cx: Scope) -> Element { - let (val, set_val) = use_state(&cx, || 0); + let val = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "hello world. Count: {val}" } button { - onclick: move |_| set_val(val + 1), + onclick: move |_| *val.make_mut() += 1, "click to increment" } } diff --git a/examples/pattern_reducer.rs b/examples/pattern_reducer.rs index 04c1811a1..beb6f414f 100644 --- a/examples/pattern_reducer.rs +++ b/examples/pattern_reducer.rs @@ -15,16 +15,16 @@ fn main() { } fn app(cx: Scope) -> Element { - let (state, set_state) = use_state(&cx, PlayerState::new); + let state = use_state(&cx, PlayerState::new); cx.render(rsx!( div { h1 {"Select an option"} h3 { "The radio is... " [state.is_playing()] "!" } - button { onclick: move |_| set_state.make_mut().reduce(PlayerAction::Pause), + button { onclick: move |_| state.make_mut().reduce(PlayerAction::Pause), "Pause" } - button { onclick: move |_| set_state.make_mut().reduce(PlayerAction::Play), + button { onclick: move |_| state.make_mut().reduce(PlayerAction::Play), "Play" } } diff --git a/examples/readme.rs b/examples/readme.rs index b37a8e07a..ede9aa654 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -9,13 +9,13 @@ fn main() { } fn app(cx: Scope) -> Element { - let (count, set_count) = use_state(&cx, || 0); + let count = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "High-Five counter: {count}" } - button { onclick: move |_| set_count(count + 1), "Up high!" } - button { onclick: move |_| set_count(count - 1), "Down low!" } + button { onclick: move |_| *count.make_mut() += 1, "Up high!" } + button { onclick: move |_| *count.make_mut() -= 1, "Down low!" } } }) } diff --git a/examples/rsx_compile_fail.rs b/examples/rsx_compile_fail.rs index 8f490bf26..20760ef5b 100644 --- a/examples/rsx_compile_fail.rs +++ b/examples/rsx_compile_fail.rs @@ -12,7 +12,7 @@ fn main() { } fn example(cx: Scope) -> Element { - let (items, _set_items) = use_state(&cx, || { + let items = use_state(&cx, || { vec![Thing { a: "asd".to_string(), b: 10, diff --git a/examples/svg.rs b/examples/svg.rs index 6e5676fe3..d924f0222 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -7,7 +7,7 @@ fn main() { } fn app(cx: Scope) -> Element { - let (val, set_val) = use_state(&cx, || 5); + let val = use_state(&cx, || 5); cx.render(rsx! { div { @@ -21,12 +21,12 @@ fn app(cx: Scope) -> Element { height: "80%", width: "80%", Die { - value: *val, + value: **val, keep: true, onclick: move |_| { use rand::Rng; let mut rng = rand::thread_rng(); - set_val(rng.gen_range(1..6)); + val.set(rng.gen_range(1..6)); } } } diff --git a/examples/tasks.rs b/examples/tasks.rs index 22b61eab8..2257b637b 100644 --- a/examples/tasks.rs +++ b/examples/tasks.rs @@ -10,14 +10,14 @@ fn main() { } fn app(cx: Scope) -> Element { - let (count, set_count) = use_state(&cx, || 0); + let count = use_state(&cx, || 0); use_future(&cx, (), move |_| { - let set_count = set_count.clone(); + let count = count.clone(); async move { loop { tokio::time::sleep(Duration::from_millis(1000)).await; - set_count.modify(|f| f + 1); + count.modify(|f| f + 1); } } }); @@ -26,7 +26,7 @@ fn app(cx: Scope) -> Element { div { h1 { "Current count: {count}" } button { - onclick: move |_| set_count(0), + onclick: move |_| count.set(0), "Reset the count" } } diff --git a/examples/textarea.rs b/examples/textarea.rs index 5988149ba..9acc732cd 100644 --- a/examples/textarea.rs +++ b/examples/textarea.rs @@ -7,7 +7,7 @@ fn main() { } fn app(cx: Scope) -> Element { - let (model, set_model) = use_state(&cx, || String::from("asd")); + let model = use_state(&cx, || String::from("asd")); println!("{}", model); @@ -17,7 +17,7 @@ fn app(cx: Scope) -> Element { rows: "10", cols: "80", value: "{model}", - oninput: move |e| set_model(e.value.clone()), + oninput: move |e| model.set(e.value.clone()), } }) } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index f0109eb4b..a031e8c8c 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -19,15 +19,15 @@ pub struct TodoItem { } pub fn app(cx: Scope<()>) -> Element { - let (todos, set_todos) = use_state(&cx, im_rc::HashMap::::default); - let (filter, set_filter) = use_state(&cx, || FilterState::All); - let (draft, set_draft) = use_state(&cx, || "".to_string()); - let (todo_id, set_todo_id) = use_state(&cx, || 0); + let todos = use_state(&cx, im_rc::HashMap::::default); + let filter = use_state(&cx, || FilterState::All); + let draft = use_state(&cx, || "".to_string()); + let todo_id = use_state(&cx, || 0); // Filter the todos based on the filter state let mut filtered_todos = todos .iter() - .filter(|(_, item)| match filter { + .filter(|(_, item)| match **filter { FilterState::All => true, FilterState::Active => !item.checked, FilterState::Completed => item.checked, @@ -54,25 +54,25 @@ pub fn app(cx: Scope<()>) -> Element { placeholder: "What needs to be done?", value: "{draft}", autofocus: "true", - oninput: move |evt| set_draft(evt.value.clone()), + oninput: move |evt| draft.set(evt.value.clone()), onkeydown: move |evt| { if evt.key == "Enter" && !draft.is_empty() { - set_todos.make_mut().insert( - *todo_id, + todos.make_mut().insert( + **todo_id, TodoItem { - id: *todo_id, + id: **todo_id, checked: false, - contents: draft.clone(), + contents: draft.to_string(), }, ); - set_todo_id(todo_id + 1); - set_draft("".to_string()); + *todo_id.make_mut() += 1; + draft.set("".to_string()); } } } } ul { class: "todo-list", - filtered_todos.iter().map(|id| rsx!(todo_entry( key: "{id}", id: *id, set_todos: set_todos ))) + filtered_todos.iter().map(|id| rsx!(todo_entry( key: "{id}", id: *id, todos: todos ))) } (!todos.is_empty()).then(|| rsx!( footer { class: "footer", @@ -81,14 +81,14 @@ pub fn app(cx: Scope<()>) -> Element { span {"{item_text} left"} } ul { class: "filters", - li { class: "All", a { onclick: move |_| set_filter(FilterState::All), "All" }} - li { class: "Active", a { onclick: move |_| set_filter(FilterState::Active), "Active" }} - li { class: "Completed", a { onclick: move |_| set_filter(FilterState::Completed), "Completed" }} + li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }} + li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }} + li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }} } (show_clear_completed).then(|| rsx!( button { class: "clear-completed", - onclick: move |_| set_todos.make_mut().retain(|_, todo| !todo.checked), + onclick: move |_| todos.make_mut().retain(|_, todo| !todo.checked), "Clear completed" } )) @@ -106,29 +106,29 @@ pub fn app(cx: Scope<()>) -> Element { #[derive(Props)] pub struct TodoEntryProps<'a> { - set_todos: &'a UseState>, + todos: &'a UseState>, id: u32, } pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element { - let (is_editing, set_is_editing) = use_state(&cx, || false); + let is_editing = use_state(&cx, || false); - let todos = cx.props.set_todos.get(); + let todos = cx.props.todos.get(); let todo = &todos[&cx.props.id]; let completed = if todo.checked { "completed" } else { "" }; - let editing = if *is_editing { "editing" } else { "" }; + let editing = if **is_editing { "editing" } else { "" }; rsx!(cx, li { class: "{completed} {editing}", div { class: "view", input { class: "toggle", r#type: "checkbox", id: "cbg-{todo.id}", checked: "{todo.checked}", onchange: move |evt| { - cx.props.set_todos.make_mut()[&cx.props.id].checked = evt.value.parse().unwrap(); + cx.props.todos.make_mut()[&cx.props.id].checked = evt.value.parse().unwrap(); } } label { r#for: "cbg-{todo.id}", - onclick: move |_| set_is_editing(true), + onclick: move |_| is_editing.set(true), "{todo.contents}" } } @@ -136,12 +136,12 @@ pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element { input { class: "edit", value: "{todo.contents}", - oninput: move |evt| cx.props.set_todos.make_mut()[&cx.props.id].contents = evt.value.clone(), + oninput: move |evt| cx.props.todos.make_mut()[&cx.props.id].contents = evt.value.clone(), autofocus: "true", - onfocusout: move |_| set_is_editing(false), + onfocusout: move |_| is_editing.set(false), onkeydown: move |evt| { match evt.key.as_str() { - "Enter" | "Escape" | "Tab" => set_is_editing(false), + "Enter" | "Escape" | "Tab" => is_editing.set(false), _ => {} } }, diff --git a/examples/window_event.rs b/examples/window_event.rs index cb578829f..0be929ebb 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -13,9 +13,9 @@ fn app(cx: Scope) -> Element { // window.set_fullscreen(true); // window.set_resizable(false); - let (fullscreen, set_fullscreen) = use_state(&cx, || false); - let (always_on_top, set_always_on_top) = use_state(&cx, || false); - let (decorations, set_decorations) = use_state(&cx, || false); + let fullscreen = use_state(&cx, || false); + let always_on_top = use_state(&cx, || false); + let decorations = use_state(&cx, || false); cx.render(rsx!( link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" } @@ -39,10 +39,9 @@ fn app(cx: Scope) -> Element { onmousedown: |evt| evt.cancel_bubble(), onclick: move |_| { - window.set_fullscreen(!fullscreen); - window.set_resizable(*fullscreen); - - set_fullscreen(!fullscreen); + window.set_fullscreen(!**fullscreen); + window.set_resizable(**fullscreen); + fullscreen.modify(|f| !*f); }, "Fullscreen" } @@ -65,7 +64,7 @@ fn app(cx: Scope) -> Element { onmousedown: |evt| evt.cancel_bubble(), onclick: move |_| { window.set_always_on_top(!always_on_top); - set_always_on_top(!always_on_top); + always_on_top.set(!always_on_top); }, "Always On Top" } @@ -76,7 +75,7 @@ fn app(cx: Scope) -> Element { onmousedown: |evt| evt.cancel_bubble(), onclick: move |_| { window.set_decorations(!decorations); - set_decorations(!decorations); + decorations.set(!decorations); }, "Set Decorations" } diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index 2790a8a89..36fc81a95 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -9,7 +9,7 @@ fn main() { } fn app(cx: Scope) -> Element { - let (contents, set_contents) = use_state(&cx, || { + let contents = use_state(&cx, || { String::from("") }); @@ -20,7 +20,7 @@ fn app(cx: Scope) -> Element { input { value: "{contents}", r#type: "text", - oninput: move |e| set_contents(e.value.clone()), + oninput: move |e| contents.set(e.value.clone()), } } }) diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 91b01330f..6e8070191 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -4,6 +4,7 @@ use dioxus_core::prelude::*; use std::{ cell::{RefCell, RefMut}, fmt::{Debug, Display}, + ops::Not, rc::Rc, sync::Arc, }; @@ -18,13 +19,13 @@ use std::{ /// /// ```ignore /// const Example: Component = |cx| { -/// let (count, set_count) = use_state(&cx, || 0); +/// let count = use_state(&cx, || 0); /// /// cx.render(rsx! { /// div { /// h1 { "Count: {count}" } -/// button { onclick: move |_| set_count(a - 1), "Increment" } -/// button { onclick: move |_| set_count(a + 1), "Decrement" } +/// button { onclick: move |_| *count.modify() += 1, "Increment" } +/// button { onclick: move |_| *count.modify() -= 1, "Decrement" } /// } /// )) /// } @@ -32,7 +33,7 @@ use std::{ pub fn use_state<'a, T: 'static>( cx: &'a ScopeState, initial_state_fn: impl FnOnce() -> T, -) -> (&'a T, &'a UseState) { +) -> &'a UseState { let hook = cx.use_hook(move |_| { let current_val = Rc::new(initial_state_fn()); let update_callback = cx.schedule_update(); @@ -65,7 +66,7 @@ pub fn use_state<'a, T: 'static>( hook.current_val = hook.slot.borrow().clone(); - (hook.current_val.as_ref(), hook) + hook } pub struct UseState { @@ -76,6 +77,11 @@ pub struct UseState { } 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 @@ -188,7 +194,12 @@ impl UseState { /// } /// ``` #[must_use] - pub fn get(&self) -> &Rc { + pub fn get(&self) -> &T { + &self.current_val + } + + #[must_use] + pub fn get_rc(&self) -> &Rc { &self.current_val } @@ -260,7 +271,7 @@ impl UseState { /// ``` /// let (val, set_val) = use_state(&cx, || 0); /// - /// set_val.with_mut(|v| *v = 1); + /// *set_val.make_mut() += 1; /// ``` #[must_use] pub fn make_mut(&self) -> RefMut { @@ -293,10 +304,22 @@ impl<'a, T: 'static + Display> std::fmt::Display for UseState { } } -impl PartialEq> for UseState { +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 { - // some level of memoization for UseState - Rc::ptr_eq(&self.slot, &other.slot) + Rc::ptr_eq(&self.current_val, &other.current_val) } } @@ -307,10 +330,57 @@ impl Debug for UseState { } impl<'a, T> std::ops::Deref for UseState { - type Target = Rc; + type Target = T; fn deref(&self) -> &Self::Target { - &self.setter + 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 } } @@ -318,16 +388,16 @@ impl<'a, T> std::ops::Deref for UseState { fn api_makes_sense() { #[allow(unused)] fn app(cx: Scope) -> Element { - let (val, set_val) = use_state(&cx, || 0); + let val = use_state(&cx, || 0); - set_val(0); - set_val.modify(|v| v + 1); - let real_current = set_val.current(); + val.set(0); + val.modify(|v| v + 1); + let real_current = val.current(); - match val { + match val.get() { 10 => { - set_val(20); - set_val.modify(|v| v + 1); + val.set(20); + val.modify(|v| v + 1); } 20 => {} _ => { @@ -336,9 +406,9 @@ fn api_makes_sense() { } cx.spawn({ - dioxus_core::to_owned![set_val]; + dioxus_core::to_owned![val]; async move { - set_val.modify(|f| f + 1); + val.modify(|f| f + 1); } });