1
0

use_state.rs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. use crate::use_hook;
  2. use std::rc::Rc;
  3. struct UseState<T2> {
  4. current: Rc<T2>,
  5. }
  6. /// This hook is used to mange state in a function component.
  7. ///
  8. /// # Example
  9. /// ```rust
  10. /// # use yew_functional::{function_component, use_state, use_ref};
  11. /// # use yew::prelude::*;
  12. /// # use std::rc::Rc;
  13. /// #
  14. /// #[function_component(UseState)]
  15. /// fn state() -> Html {
  16. /// let (
  17. /// counter, // the returned state
  18. /// set_counter // setter to update the state
  19. /// ) = use_state(|| 0);
  20. /// let onclick = {
  21. /// let counter = Rc::clone(&counter);
  22. /// Callback::from(move |_| set_counter(*counter + 1))
  23. /// };
  24. ///
  25. /// html! {
  26. /// <div>
  27. /// <button onclick=onclick>{ "Increment value" }</button>
  28. /// <p>
  29. /// <b>{ "Current value: " }</b>
  30. /// { counter }
  31. /// </p>
  32. /// </div>
  33. /// }
  34. /// }
  35. /// ```
  36. pub fn use_state<T: 'static, F: FnOnce() -> T + 'static>(
  37. initial_state_fn: F,
  38. ) -> (Rc<T>, Rc<dyn Fn(T)>) {
  39. use_hook(
  40. // Initializer
  41. move || UseState {
  42. current: Rc::new(initial_state_fn()),
  43. },
  44. // Runner
  45. move |hook, updater| {
  46. let setter: Rc<(dyn Fn(T))> = Rc::new(move |new_val: T| {
  47. updater.callback(move |st: &mut UseState<T>| {
  48. st.current = Rc::new(new_val);
  49. true
  50. })
  51. });
  52. let current = hook.current.clone();
  53. (current, setter)
  54. },
  55. // Teardown
  56. |_| {},
  57. )
  58. }
  59. #[cfg(test)]
  60. mod tests {
  61. use super::*;
  62. use crate::hooks::use_effect_with_deps;
  63. use crate::util::*;
  64. use crate::{FunctionComponent, FunctionProvider};
  65. use wasm_bindgen_test::*;
  66. use yew::prelude::*;
  67. wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
  68. #[wasm_bindgen_test]
  69. fn use_state_works() {
  70. struct UseStateFunction {}
  71. impl FunctionProvider for UseStateFunction {
  72. type TProps = ();
  73. fn run(_: &Self::TProps) -> Html {
  74. let (counter, set_counter) = use_state(|| 0);
  75. if *counter < 5 {
  76. set_counter(*counter + 1)
  77. }
  78. return html! {
  79. <div>
  80. {"Test Output: "}
  81. <div id="result">{*counter}</div>
  82. {"\n"}
  83. </div>
  84. };
  85. }
  86. }
  87. type UseComponent = FunctionComponent<UseStateFunction>;
  88. let app: App<UseComponent> = yew::App::new();
  89. app.mount(yew::utils::document().get_element_by_id("output").unwrap());
  90. let result = obtain_result();
  91. assert_eq!(result.as_str(), "5");
  92. }
  93. #[wasm_bindgen_test]
  94. fn multiple_use_state_setters() {
  95. struct UseStateFunction {}
  96. impl FunctionProvider for UseStateFunction {
  97. type TProps = ();
  98. fn run(_: &Self::TProps) -> Html {
  99. let (counter, set_counter_in_use_effect) = use_state(|| 0);
  100. let counter = *counter;
  101. // clone without manually wrapping with Rc
  102. let set_counter_in_another_scope = set_counter_in_use_effect.clone();
  103. use_effect_with_deps(
  104. move |_| {
  105. // 1st location
  106. set_counter_in_use_effect(counter + 1);
  107. || {}
  108. },
  109. (),
  110. );
  111. let another_scope = move || {
  112. if counter < 11 {
  113. // 2nd location
  114. set_counter_in_another_scope(counter + 10)
  115. }
  116. };
  117. another_scope();
  118. return html! {
  119. <div>
  120. {"Test Output: "}
  121. // expected output
  122. <div id="result">{counter}</div>
  123. {"\n"}
  124. </div>
  125. };
  126. }
  127. }
  128. type UseComponent = FunctionComponent<UseStateFunction>;
  129. let app: App<UseComponent> = yew::App::new();
  130. app.mount(yew::utils::document().get_element_by_id("output").unwrap());
  131. let result = obtain_result();
  132. assert_eq!(result.as_str(), "11");
  133. }
  134. }