Selaa lähdekoodia

feat: re-unify set_state

Jonathan Kelley 3 vuotta sitten
vanhempi
commit
6c3f5195f4

+ 24 - 32
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();
 

+ 18 - 18
examples/crm.rs

@@ -21,10 +21,10 @@ pub struct Client {
 
 fn app(cx: Scope) -> Element {
     let clients = use_ref(&cx, || vec![] as Vec<Client>);
-    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"
                         }
                     }

+ 5 - 3
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 {

+ 5 - 5
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"),
                         }

+ 2 - 2
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",

+ 2 - 2
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"
             }
         }

+ 3 - 3
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"
             }
         }

+ 3 - 3
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!" }
         }
     })
 }

+ 1 - 1
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,

+ 3 - 3
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));
                     }
                 }
             }

+ 4 - 4
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"
             }
         }

+ 2 - 2
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()),
         }
     })
 }

+ 26 - 26
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::<u32, TodoItem>::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::<u32, TodoItem>::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<im_rc::HashMap<u32, TodoItem>>,
+    todos: &'a UseState<im_rc::HashMap<u32, TodoItem>>,
     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),
                         _ => {}
                     }
                 },

+ 8 - 9
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"
                     }

+ 2 - 2
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("<script>alert(\"hello world\")</script>")
     });
 
@@ -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()),
             }
         }
     })

+ 91 - 21
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<T>) {
+) -> &'a UseState<T> {
     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<T: 'static> {
@@ -76,6 +77,11 @@ pub struct UseState<T: 'static> {
 }
 
 impl<T: 'static> UseState<T> {
+    /// 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<T: 'static> UseState<T> {
     /// }
     /// ```
     #[must_use]
-    pub fn get(&self) -> &Rc<T> {
+    pub fn get(&self) -> &T {
+        &self.current_val
+    }
+
+    #[must_use]
+    pub fn get_rc(&self) -> &Rc<T> {
         &self.current_val
     }
 
@@ -260,7 +271,7 @@ impl<T: Clone> UseState<T> {
     /// ```
     /// 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<T> {
@@ -293,10 +304,22 @@ impl<'a, T: 'static + Display> std::fmt::Display for UseState<T> {
     }
 }
 
-impl<T> PartialEq<UseState<T>> for UseState<T> {
+impl<T: PartialEq> PartialEq<T> for UseState<T> {
+    fn eq(&self, other: &T) -> bool {
+        self.current_val.as_ref() == other
+    }
+}
+
+// todo: this but for more interesting conrete types
+impl PartialEq<bool> for &UseState<bool> {
+    fn eq(&self, other: &bool) -> bool {
+        self.current_val.as_ref() == other
+    }
+}
+
+impl<T: PartialEq> PartialEq<UseState<T>> for UseState<T> {
     fn eq(&self, other: &UseState<T>) -> 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<T: Debug> Debug for UseState<T> {
 }
 
 impl<'a, T> std::ops::Deref for UseState<T> {
-    type Target = Rc<dyn Fn(T)>;
+    type Target = T;
 
     fn deref(&self) -> &Self::Target {
-        &self.setter
+        self.current_val.as_ref()
+    }
+}
+
+impl<T: Not + Copy> std::ops::Not for &UseState<T> {
+    type Output = <T as std::ops::Not>::Output;
+
+    fn not(self) -> Self::Output {
+        self.current_val.not()
+    }
+}
+
+impl<T: Not + Copy> std::ops::Not for UseState<T> {
+    type Output = <T as std::ops::Not>::Output;
+
+    fn not(self) -> Self::Output {
+        self.current_val.not()
+    }
+}
+
+impl<T: std::ops::Add + Copy> std::ops::Add<T> for &UseState<T> {
+    type Output = <T as std::ops::Add>::Output;
+
+    fn add(self, other: T) -> Self::Output {
+        *self.current_val.as_ref() + other
+    }
+}
+impl<T: std::ops::Sub + Copy> std::ops::Sub<T> for &UseState<T> {
+    type Output = <T as std::ops::Sub>::Output;
+
+    fn sub(self, other: T) -> Self::Output {
+        *self.current_val.as_ref() - other
+    }
+}
+
+impl<T: std::ops::Div + Copy> std::ops::Div<T> for &UseState<T> {
+    type Output = <T as std::ops::Div>::Output;
+
+    fn div(self, other: T) -> Self::Output {
+        *self.current_val.as_ref() / other
+    }
+}
+
+impl<T: std::ops::Mul + Copy> std::ops::Mul<T> for &UseState<T> {
+    type Output = <T as std::ops::Mul>::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<T> {
 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);
             }
         });