mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-16 21:58:25 +00:00
feat: re-unify set_state
This commit is contained in:
parent
3928856a9b
commit
6c3f5195f4
16 changed files with 199 additions and 136 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,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 {
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!" }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue