mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Pass hook dependencies as the first function argument (#2861)
* Add use_effect_with * Fix doc * Add NeverEq * Save as deps-and-runner * remove with_deps * fix other use_effect_with_deps * add migration guide * fix website * fix doc test * return use_effect_base * fix docs * fmt * fix doc tests * noeq * use_callback * finsihing touches * fmt * nighly fmt * fix mistake --------- Co-authored-by: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com>
This commit is contained in:
parent
bdf5712d96
commit
6f4cdf2802
@ -19,39 +19,36 @@ pub fn App() -> Html {
|
||||
let flip_back_timer: Rc<RefCell<Option<Timeout>>> = use_mut_ref(|| None);
|
||||
let sec_past_time = *sec_past;
|
||||
|
||||
use_effect_with_deps(
|
||||
move |state| {
|
||||
// game reset
|
||||
if state.status == Status::Ready {
|
||||
sec_past.set(0);
|
||||
}
|
||||
// game start
|
||||
else if *sec_past == 0 && state.last_card.is_some() {
|
||||
let sec_past = sec_past.clone();
|
||||
let mut sec = *sec_past;
|
||||
*sec_past_timer.borrow_mut() = Some(Interval::new(1000, move || {
|
||||
sec += 1;
|
||||
sec_past.set(sec);
|
||||
}));
|
||||
}
|
||||
// game over
|
||||
else if state.status == Status::Passed {
|
||||
*sec_past_timer.borrow_mut() = None;
|
||||
*flip_back_timer.borrow_mut() = None;
|
||||
state.dispatch(Action::TrySaveBestScore(*sec_past));
|
||||
}
|
||||
// match failed
|
||||
else if state.rollback_cards.is_some() {
|
||||
let cloned_state = state.clone();
|
||||
let cloned_rollback_cards = state.rollback_cards.clone().unwrap();
|
||||
*flip_back_timer.borrow_mut() = Some(Timeout::new(1000, move || {
|
||||
cloned_state.dispatch(Action::RollbackCards(cloned_rollback_cards));
|
||||
}));
|
||||
}
|
||||
|| ()
|
||||
},
|
||||
state.clone(),
|
||||
);
|
||||
use_effect_with(state.clone(), move |state| {
|
||||
// game reset
|
||||
if state.status == Status::Ready {
|
||||
sec_past.set(0);
|
||||
}
|
||||
// game start
|
||||
else if *sec_past == 0 && state.last_card.is_some() {
|
||||
let sec_past = sec_past.clone();
|
||||
let mut sec = *sec_past;
|
||||
*sec_past_timer.borrow_mut() = Some(Interval::new(1000, move || {
|
||||
sec += 1;
|
||||
sec_past.set(sec);
|
||||
}));
|
||||
}
|
||||
// game over
|
||||
else if state.status == Status::Passed {
|
||||
*sec_past_timer.borrow_mut() = None;
|
||||
*flip_back_timer.borrow_mut() = None;
|
||||
state.dispatch(Action::TrySaveBestScore(*sec_past));
|
||||
}
|
||||
// match failed
|
||||
else if state.rollback_cards.is_some() {
|
||||
let cloned_state = state.clone();
|
||||
let cloned_rollback_cards = state.rollback_cards.clone().unwrap();
|
||||
*flip_back_timer.borrow_mut() = Some(Timeout::new(1000, move || {
|
||||
cloned_state.dispatch(Action::RollbackCards(cloned_rollback_cards));
|
||||
}));
|
||||
}
|
||||
|| ()
|
||||
});
|
||||
|
||||
let on_reset = {
|
||||
let state = state.clone();
|
||||
|
||||
@ -38,14 +38,11 @@ pub fn AuthorCard(props: &Props) -> Html {
|
||||
|
||||
{
|
||||
let author_dispatcher = author.dispatcher();
|
||||
use_effect_with_deps(
|
||||
move |seed| {
|
||||
author_dispatcher.dispatch(*seed);
|
||||
use_effect_with(seed, move |seed| {
|
||||
author_dispatcher.dispatch(*seed);
|
||||
|
||||
|| {}
|
||||
},
|
||||
seed,
|
||||
);
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
|
||||
let author = &author.inner;
|
||||
|
||||
@ -38,14 +38,11 @@ pub fn PostCard(props: &Props) -> Html {
|
||||
|
||||
{
|
||||
let post_dispatcher = post.dispatcher();
|
||||
use_effect_with_deps(
|
||||
move |seed| {
|
||||
post_dispatcher.dispatch(*seed);
|
||||
use_effect_with(seed, move |seed| {
|
||||
post_dispatcher.dispatch(*seed);
|
||||
|
||||
|| {}
|
||||
},
|
||||
seed,
|
||||
);
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
|
||||
let post = &post.inner;
|
||||
|
||||
@ -81,28 +81,22 @@ pub fn ProgressDelay(props: &Props) -> Html {
|
||||
|
||||
{
|
||||
let value = value.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
let interval = (duration_ms / RESOLUTION).min(MIN_INTERVAL_MS);
|
||||
let interval = Interval::new(interval, move || value.dispatch(ValueAction::Tick));
|
||||
use_effect_with((), move |_| {
|
||||
let interval = (duration_ms / RESOLUTION).min(MIN_INTERVAL_MS);
|
||||
let interval = Interval::new(interval, move || value.dispatch(ValueAction::Tick));
|
||||
|
||||
|| {
|
||||
let _interval = interval;
|
||||
}
|
||||
},
|
||||
(),
|
||||
);
|
||||
|| {
|
||||
let _interval = interval;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let value = value.clone();
|
||||
use_effect_with_deps(
|
||||
move |props| {
|
||||
value.dispatch(ValueAction::Props(props.clone()));
|
||||
|| {}
|
||||
},
|
||||
props.clone(),
|
||||
);
|
||||
use_effect_with(props.clone(), move |props| {
|
||||
value.dispatch(ValueAction::Props(props.clone()));
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
|
||||
let value = &value.value;
|
||||
|
||||
@ -19,14 +19,11 @@ pub fn Author(props: &Props) -> Html {
|
||||
|
||||
{
|
||||
let author_dispatcher = author.dispatcher();
|
||||
use_effect_with_deps(
|
||||
move |seed| {
|
||||
author_dispatcher.dispatch(*seed);
|
||||
use_effect_with(seed, move |seed| {
|
||||
author_dispatcher.dispatch(*seed);
|
||||
|
||||
|| {}
|
||||
},
|
||||
seed,
|
||||
);
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
|
||||
let author = &author.inner;
|
||||
|
||||
@ -38,14 +38,11 @@ pub fn Post(props: &Props) -> Html {
|
||||
|
||||
{
|
||||
let post_dispatcher = post.dispatcher();
|
||||
use_effect_with_deps(
|
||||
move |seed| {
|
||||
post_dispatcher.dispatch(*seed);
|
||||
use_effect_with(seed, move |seed| {
|
||||
post_dispatcher.dispatch(*seed);
|
||||
|
||||
|| {}
|
||||
},
|
||||
seed,
|
||||
);
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
|
||||
let post = &post.inner;
|
||||
|
||||
@ -22,13 +22,10 @@ fn app() -> Html {
|
||||
});
|
||||
|
||||
// Effect
|
||||
use_effect_with_deps(
|
||||
move |state| {
|
||||
LocalStorage::set(KEY, &state.clone().entries).expect("failed to set");
|
||||
|| ()
|
||||
},
|
||||
state.clone(),
|
||||
);
|
||||
use_effect_with(state.clone(), move |state| {
|
||||
LocalStorage::set(KEY, &state.clone().entries).expect("failed to set");
|
||||
|| ()
|
||||
});
|
||||
|
||||
// Callbacks
|
||||
let onremove = {
|
||||
|
||||
@ -11,7 +11,7 @@ static WASM_BINDGEN_SNIPPETS_PATH: OnceCell<String> = OnceCell::new();
|
||||
|
||||
#[function_component]
|
||||
fn Important() -> Html {
|
||||
let msg = use_memo(|_| bindings::hello(), ());
|
||||
let msg = use_memo((), |_| bindings::hello());
|
||||
html! {
|
||||
<>
|
||||
<h2>{"Important"}</h2>
|
||||
|
||||
@ -84,26 +84,23 @@ fn base_router(props: &RouterProps) -> Html {
|
||||
{
|
||||
let loc_ctx_dispatcher = loc_ctx.dispatcher();
|
||||
|
||||
use_effect_with_deps(
|
||||
move |history| {
|
||||
use_effect_with(history, move |history| {
|
||||
let history = history.clone();
|
||||
// Force location update when history changes.
|
||||
loc_ctx_dispatcher.dispatch(history.location());
|
||||
|
||||
let history_cb = {
|
||||
let history = history.clone();
|
||||
// Force location update when history changes.
|
||||
loc_ctx_dispatcher.dispatch(history.location());
|
||||
move || loc_ctx_dispatcher.dispatch(history.location())
|
||||
};
|
||||
|
||||
let history_cb = {
|
||||
let history = history.clone();
|
||||
move || loc_ctx_dispatcher.dispatch(history.location())
|
||||
};
|
||||
let listener = history.listen(history_cb);
|
||||
|
||||
let listener = history.listen(history_cb);
|
||||
|
||||
// We hold the listener in the destructor.
|
||||
move || {
|
||||
std::mem::drop(listener);
|
||||
}
|
||||
},
|
||||
history,
|
||||
);
|
||||
// We hold the listener in the destructor.
|
||||
move || {
|
||||
std::mem::drop(listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
html! {
|
||||
|
||||
@ -38,19 +38,13 @@ use crate::functional::{hook, use_memo};
|
||||
/// };
|
||||
///
|
||||
/// // This callback depends on (), so it's created only once, then MyComponent
|
||||
/// // will be rendered only once even when you click the button mutiple times.
|
||||
/// let callback = use_callback(move |name, _| format!("Hello, {}!", name), ());
|
||||
/// // will be rendered only once even when you click the button multiple times.
|
||||
/// let callback = use_callback((), move |name, _| format!("Hello, {}!", name));
|
||||
///
|
||||
/// // It can also be used for events, this callback depends on `counter`.
|
||||
/// let oncallback = {
|
||||
/// let counter = counter.clone();
|
||||
/// use_callback(
|
||||
/// move |_e, counter| {
|
||||
/// let _ = **counter;
|
||||
/// },
|
||||
/// counter,
|
||||
/// )
|
||||
/// };
|
||||
/// let oncallback = use_callback(counter.clone(), move |_e, counter| {
|
||||
/// let _ = **counter;
|
||||
/// });
|
||||
///
|
||||
/// html! {
|
||||
/// <div>
|
||||
@ -66,7 +60,7 @@ use crate::functional::{hook, use_memo};
|
||||
/// }
|
||||
/// ```
|
||||
#[hook]
|
||||
pub fn use_callback<IN, OUT, F, D>(f: F, deps: D) -> Callback<IN, OUT>
|
||||
pub fn use_callback<IN, OUT, F, D>(deps: D, f: F) -> Callback<IN, OUT>
|
||||
where
|
||||
IN: 'static,
|
||||
OUT: 'static,
|
||||
@ -75,13 +69,10 @@ where
|
||||
{
|
||||
let deps = Rc::new(deps);
|
||||
|
||||
(*use_memo(
|
||||
move |deps| {
|
||||
let deps = deps.clone();
|
||||
let f = move |value: IN| f(value, deps.as_ref());
|
||||
Callback::from(f)
|
||||
},
|
||||
deps,
|
||||
))
|
||||
(*use_memo(deps, move |deps| {
|
||||
let deps = deps.clone();
|
||||
let f = move |value: IN| f(value, deps.as_ref());
|
||||
Callback::from(f)
|
||||
}))
|
||||
.clone()
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ where
|
||||
/// This hook is similar to [`use_effect`] but it accepts dependencies.
|
||||
///
|
||||
/// Whenever the dependencies are changed, the effect callback is called again.
|
||||
/// To detect changes, dependencies must implement `PartialEq`.
|
||||
/// To detect changes, dependencies must implement [`PartialEq`].
|
||||
///
|
||||
/// # Note
|
||||
/// The destructor also runs when dependencies change.
|
||||
@ -186,7 +186,7 @@ where
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use yew::{function_component, html, use_effect_with_deps, Html, Properties};
|
||||
/// use yew::{function_component, html, use_effect_with, Html, Properties};
|
||||
/// # use gloo::console::log;
|
||||
///
|
||||
/// #[derive(Properties, PartialEq)]
|
||||
@ -198,14 +198,13 @@ where
|
||||
/// fn HelloWorld(props: &Props) -> Html {
|
||||
/// let is_loading = props.is_loading.clone();
|
||||
///
|
||||
/// use_effect_with_deps(
|
||||
/// move |_| {
|
||||
/// log!(" Is loading prop changed!");
|
||||
/// },
|
||||
/// is_loading,
|
||||
/// );
|
||||
/// use_effect_with(is_loading, move |_| {
|
||||
/// log!(" Is loading prop changed!");
|
||||
/// });
|
||||
///
|
||||
/// html! { <>{"Am I loading? - "}{is_loading}</> }
|
||||
/// html! {
|
||||
/// <>{"Am I loading? - "}{is_loading}</>
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@ -217,17 +216,14 @@ where
|
||||
/// render of a component.
|
||||
///
|
||||
/// ```rust
|
||||
/// use yew::{function_component, html, use_effect_with_deps, Html};
|
||||
/// use yew::{function_component, html, use_effect_with, Html};
|
||||
/// # use gloo::console::log;
|
||||
///
|
||||
/// #[function_component]
|
||||
/// fn HelloWorld() -> Html {
|
||||
/// use_effect_with_deps(
|
||||
/// move |_| {
|
||||
/// log!("I got rendered, yay!");
|
||||
/// },
|
||||
/// (),
|
||||
/// );
|
||||
/// use_effect_with((), move |_| {
|
||||
/// log!("I got rendered, yay!");
|
||||
/// });
|
||||
///
|
||||
/// html! { "Hello" }
|
||||
/// }
|
||||
@ -239,19 +235,17 @@ where
|
||||
/// It will only get called when the component is removed from view / gets destroyed.
|
||||
///
|
||||
/// ```rust
|
||||
/// use yew::{function_component, html, use_effect_with_deps, Html};
|
||||
/// use yew::{function_component, html, use_effect_with, Html};
|
||||
/// # use gloo::console::log;
|
||||
///
|
||||
/// #[function_component]
|
||||
/// fn HelloWorld() -> Html {
|
||||
/// use_effect_with_deps(
|
||||
/// move |_| {
|
||||
/// || {
|
||||
/// log!("Noo dont kill me, ahhh!");
|
||||
/// }
|
||||
/// },
|
||||
/// (),
|
||||
/// );
|
||||
/// use_effect_with((), move |_| {
|
||||
/// || {
|
||||
/// log!("Noo dont kill me, ahhh!");
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// html! { "Hello" }
|
||||
/// }
|
||||
/// ```
|
||||
@ -261,8 +255,7 @@ where
|
||||
/// ### Tip
|
||||
///
|
||||
/// The callback can return [`()`] if there is no destructor to run.
|
||||
#[hook]
|
||||
pub fn use_effect_with_deps<T, F, D>(f: F, deps: T)
|
||||
pub fn use_effect_with<T, F, D>(deps: T, f: F) -> impl Hook<Output = ()>
|
||||
where
|
||||
T: PartialEq + 'static,
|
||||
F: FnOnce(&T) -> D + 'static,
|
||||
|
||||
@ -59,10 +59,9 @@ where
|
||||
/// #[function_component(UseMemo)]
|
||||
/// fn memo(props: &Props) -> Html {
|
||||
/// // Will only get recalculated if `props.step` value changes
|
||||
/// let message = use_memo(
|
||||
/// |step| format!("{}. Do Some Expensive Calculation", step),
|
||||
/// props.step,
|
||||
/// );
|
||||
/// let message = use_memo(props.step, |step| {
|
||||
/// format!("{}. Do Some Expensive Calculation", step)
|
||||
/// });
|
||||
///
|
||||
/// html! {
|
||||
/// <div>
|
||||
@ -72,7 +71,7 @@ where
|
||||
/// }
|
||||
/// ```
|
||||
#[hook]
|
||||
pub fn use_memo<T, F, D>(f: F, deps: D) -> Rc<T>
|
||||
pub fn use_memo<T, F, D>(deps: D, f: F) -> Rc<T>
|
||||
where
|
||||
T: 'static,
|
||||
F: FnOnce(&D) -> T,
|
||||
|
||||
@ -46,7 +46,7 @@ where
|
||||
|
||||
let state = {
|
||||
let deps = deps.clone();
|
||||
use_memo(move |_| f(deps), ()).run(ctx)
|
||||
use_memo((), move |_| f(deps)).run(ctx)
|
||||
};
|
||||
|
||||
let state = PreparedStateBase {
|
||||
|
||||
@ -297,7 +297,7 @@ where
|
||||
///
|
||||
/// The dispatch function is guaranteed to be the same across the entire
|
||||
/// component lifecycle. You can safely omit the `UseReducerHandle` from the
|
||||
/// dependents of `use_effect_with_deps` if you only intend to dispatch
|
||||
/// dependents of `use_effect_with` if you only intend to dispatch
|
||||
/// values from within the hooks.
|
||||
///
|
||||
/// # Caution
|
||||
@ -305,7 +305,7 @@ where
|
||||
/// The value held in the handle will reflect the value of at the time the
|
||||
/// handle is returned by the `use_reducer`. It is possible that the handle does
|
||||
/// not dereference to an up to date value if you are moving it into a
|
||||
/// `use_effect_with_deps` hook. You can register the
|
||||
/// `use_effect_with` hook. You can register the
|
||||
/// state to the dependents so the hook can be updated when the value changes.
|
||||
#[hook]
|
||||
pub fn use_reducer<T, F>(init_fn: F) -> UseReducerHandle<T>
|
||||
|
||||
@ -83,7 +83,7 @@ where
|
||||
/// use wasm_bindgen::prelude::Closure;
|
||||
/// use wasm_bindgen::JsCast;
|
||||
/// use web_sys::{Event, HtmlElement};
|
||||
/// use yew::{function_component, html, use_effect_with_deps, use_node_ref, Html};
|
||||
/// use yew::{function_component, html, use_effect_with, use_node_ref, Html};
|
||||
///
|
||||
/// #[function_component(UseNodeRef)]
|
||||
/// pub fn node_ref_hook() -> Html {
|
||||
@ -92,32 +92,26 @@ where
|
||||
/// {
|
||||
/// let div_ref = div_ref.clone();
|
||||
///
|
||||
/// use_effect_with_deps(
|
||||
/// |div_ref| {
|
||||
/// let div = div_ref
|
||||
/// .cast::<HtmlElement>()
|
||||
/// .expect("div_ref not attached to div element");
|
||||
/// use_effect_with(div_ref, |div_ref| {
|
||||
/// let div = div_ref
|
||||
/// .cast::<HtmlElement>()
|
||||
/// .expect("div_ref not attached to div element");
|
||||
///
|
||||
/// let listener = Closure::<dyn Fn(Event)>::wrap(Box::new(|_| {
|
||||
/// web_sys::console::log_1(&"Clicked!".into());
|
||||
/// }));
|
||||
/// let listener = Closure::<dyn Fn(Event)>::wrap(Box::new(|_| {
|
||||
/// web_sys::console::log_1(&"Clicked!".into());
|
||||
/// }));
|
||||
///
|
||||
/// div.add_event_listener_with_callback(
|
||||
/// div.add_event_listener_with_callback("click", listener.as_ref().unchecked_ref())
|
||||
/// .unwrap();
|
||||
///
|
||||
/// move || {
|
||||
/// div.remove_event_listener_with_callback(
|
||||
/// "click",
|
||||
/// listener.as_ref().unchecked_ref(),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
///
|
||||
/// move || {
|
||||
/// div.remove_event_listener_with_callback(
|
||||
/// "click",
|
||||
/// listener.as_ref().unchecked_ref(),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
/// }
|
||||
/// },
|
||||
/// div_ref,
|
||||
/// );
|
||||
/// }
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// html! {
|
||||
@ -131,7 +125,7 @@ where
|
||||
/// # Tip
|
||||
///
|
||||
/// When conditionally rendering elements you can use `NodeRef` in conjunction with
|
||||
/// `use_effect_with_deps` to perform actions each time an element is rendered and just before the
|
||||
/// `use_effect_with` to perform actions each time an element is rendered and just before the
|
||||
/// component where the hook is used in is going to be removed from the DOM.
|
||||
#[hook]
|
||||
pub fn use_node_ref() -> NodeRef {
|
||||
|
||||
@ -63,14 +63,14 @@ where
|
||||
/// The value held in the handle will reflect the value of at the time the
|
||||
/// handle is returned by the `use_reducer`. It is possible that the handle does
|
||||
/// not dereference to an up to date value if you are moving it into a
|
||||
/// `use_effect_with_deps` hook. You can register the
|
||||
/// `use_effect_with` hook. You can register the
|
||||
/// state to the dependents so the hook can be updated when the value changes.
|
||||
///
|
||||
/// # Tip
|
||||
///
|
||||
/// The setter function is guaranteed to be the same across the entire
|
||||
/// component lifecycle. You can safely omit the `UseStateHandle` from the
|
||||
/// dependents of `use_effect_with_deps` if you only intend to set
|
||||
/// dependents of `use_effect_with` if you only intend to set
|
||||
/// values from within the hook.
|
||||
#[hook]
|
||||
pub fn use_state<T, F>(init_fn: F) -> UseStateHandle<T>
|
||||
|
||||
@ -922,13 +922,10 @@ async fn hydration_props_blocked_until_hydrated() {
|
||||
let range = use_state(|| 0u32..2);
|
||||
{
|
||||
let range = range.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
range.set(0..3);
|
||||
|| ()
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
range.set(0..3);
|
||||
|| ()
|
||||
});
|
||||
}
|
||||
|
||||
html! {
|
||||
@ -985,13 +982,10 @@ async fn hydrate_empty() {
|
||||
let trigger = use_state(|| false);
|
||||
{
|
||||
let trigger = trigger.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
trigger.set(true);
|
||||
|| {}
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
trigger.set(true);
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
if *trigger {
|
||||
html! { <div>{"after"}</div> }
|
||||
|
||||
@ -20,16 +20,13 @@ async fn change_nested_after_append() {
|
||||
|
||||
{
|
||||
let delayed_trigger = delayed_trigger.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
spawn_local(async move {
|
||||
sleep(Duration::from_millis(50)).await;
|
||||
delayed_trigger.set(false);
|
||||
});
|
||||
|| {}
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
spawn_local(async move {
|
||||
sleep(Duration::from_millis(50)).await;
|
||||
delayed_trigger.set(false);
|
||||
});
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
|
||||
if *delayed_trigger {
|
||||
@ -51,13 +48,10 @@ async fn change_nested_after_append() {
|
||||
{
|
||||
let show_bottom = show_bottom.clone();
|
||||
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
show_bottom.set(true);
|
||||
|| {}
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
show_bottom.set(true);
|
||||
|| {}
|
||||
});
|
||||
}
|
||||
|
||||
html! {
|
||||
|
||||
@ -703,15 +703,12 @@ async fn test_suspend_forever() {
|
||||
|
||||
{
|
||||
let page_setter = page.setter();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
spawn_local(async move {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
page_setter.set(2);
|
||||
});
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
spawn_local(async move {
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
page_setter.set(2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let content = if *page == 1 {
|
||||
|
||||
@ -43,7 +43,7 @@ async fn use_callback_works() {
|
||||
fn use_callback_comp() -> Html {
|
||||
let state = use_state(|| 0);
|
||||
|
||||
let callback = use_callback(move |name, _| format!("Hello, {}!", name), ());
|
||||
let callback = use_callback((), move |name, _| format!("Hello, {}!", name));
|
||||
|
||||
use_effect(move || {
|
||||
if *state < 5 {
|
||||
|
||||
@ -39,14 +39,11 @@ async fn use_effect_destroys_on_component_drop() {
|
||||
fn use_effect_comp(props: &FunctionProps) -> Html {
|
||||
let effect_called = props.effect_called.clone();
|
||||
let destroy_called = props.destroy_called.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
effect_called();
|
||||
#[allow(clippy::redundant_closure)] // Otherwise there is a build error
|
||||
move || destroy_called()
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
effect_called();
|
||||
#[allow(clippy::redundant_closure)] // Otherwise there is a build error
|
||||
move || destroy_called()
|
||||
});
|
||||
html! {}
|
||||
}
|
||||
|
||||
@ -87,15 +84,12 @@ async fn use_effect_works_many_times() {
|
||||
let counter = use_state(|| 0);
|
||||
let counter_clone = counter.clone();
|
||||
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
if *counter_clone < 4 {
|
||||
counter_clone.set(*counter_clone + 1);
|
||||
}
|
||||
|| {}
|
||||
},
|
||||
*counter,
|
||||
);
|
||||
use_effect_with(*counter, move |_| {
|
||||
if *counter_clone < 4 {
|
||||
counter_clone.set(*counter_clone + 1);
|
||||
}
|
||||
|| {}
|
||||
});
|
||||
|
||||
html! {
|
||||
<div>
|
||||
@ -123,13 +117,10 @@ async fn use_effect_works_once() {
|
||||
let counter = use_state(|| 0);
|
||||
let counter_clone = counter.clone();
|
||||
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
counter_clone.set(*counter_clone + 1);
|
||||
|| panic!("Destructor should not have been called")
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
counter_clone.set(*counter_clone + 1);
|
||||
|| panic!("Destructor should not have been called")
|
||||
});
|
||||
|
||||
html! {
|
||||
<div>
|
||||
@ -161,24 +152,21 @@ async fn use_effect_refires_on_dependency_change() {
|
||||
let number_ref2_c = number_ref2.clone();
|
||||
let arg = *number_ref.borrow_mut().deref_mut();
|
||||
let counter = use_state(|| 0);
|
||||
use_effect_with_deps(
|
||||
move |dep| {
|
||||
let mut ref_mut = number_ref_c.borrow_mut();
|
||||
let inner_ref_mut = ref_mut.deref_mut();
|
||||
if *inner_ref_mut < 1 {
|
||||
*inner_ref_mut += 1;
|
||||
assert_eq!(dep, &0);
|
||||
} else {
|
||||
assert_eq!(dep, &1);
|
||||
}
|
||||
counter.set(10); // we just need to make sure it does not panic
|
||||
move || {
|
||||
counter.set(11);
|
||||
*number_ref2_c.borrow_mut().deref_mut() += 1;
|
||||
}
|
||||
},
|
||||
arg,
|
||||
);
|
||||
use_effect_with(arg, move |dep| {
|
||||
let mut ref_mut = number_ref_c.borrow_mut();
|
||||
let inner_ref_mut = ref_mut.deref_mut();
|
||||
if *inner_ref_mut < 1 {
|
||||
*inner_ref_mut += 1;
|
||||
assert_eq!(dep, &0);
|
||||
} else {
|
||||
assert_eq!(dep, &1);
|
||||
}
|
||||
counter.set(10); // we just need to make sure it does not panic
|
||||
move || {
|
||||
counter.set(11);
|
||||
*number_ref2_c.borrow_mut().deref_mut() += 1;
|
||||
}
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{"The test result is"}
|
||||
|
||||
@ -19,18 +19,15 @@ async fn use_memo_works() {
|
||||
fn use_memo_comp() -> Html {
|
||||
let state = use_state(|| 0);
|
||||
|
||||
let memoed_val = use_memo(
|
||||
|_| {
|
||||
static CTR: AtomicBool = AtomicBool::new(false);
|
||||
let memoed_val = use_memo((), |_| {
|
||||
static CTR: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
if CTR.swap(true, Ordering::Relaxed) {
|
||||
panic!("multiple times rendered!");
|
||||
}
|
||||
if CTR.swap(true, Ordering::Relaxed) {
|
||||
panic!("multiple times rendered!");
|
||||
}
|
||||
|
||||
"true"
|
||||
},
|
||||
(),
|
||||
);
|
||||
"true"
|
||||
});
|
||||
|
||||
use_effect(move || {
|
||||
if *state < 5 {
|
||||
|
||||
@ -40,13 +40,10 @@ async fn use_reducer_works() {
|
||||
let counter = use_reducer(|| CounterState { counter: 10 });
|
||||
|
||||
let counter_clone = counter.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
counter_clone.dispatch(1);
|
||||
|| {}
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
counter_clone.dispatch(1);
|
||||
|| {}
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{"The test result is"}
|
||||
|
||||
@ -43,14 +43,11 @@ async fn multiple_use_state_setters() {
|
||||
fn use_state_comp() -> Html {
|
||||
let counter = use_state(|| 0);
|
||||
let counter_clone = counter.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
// 1st location
|
||||
counter_clone.set(*counter_clone + 1);
|
||||
|| {}
|
||||
},
|
||||
(),
|
||||
);
|
||||
use_effect_with((), move |_| {
|
||||
// 1st location
|
||||
counter_clone.set(*counter_clone + 1);
|
||||
|| {}
|
||||
});
|
||||
let another_scope = {
|
||||
let counter = counter.clone();
|
||||
move || {
|
||||
|
||||
@ -59,7 +59,7 @@ async fn no_main() {
|
||||
The recommended way of working with server-side rendering is
|
||||
function components.
|
||||
|
||||
All hooks other than `use_effect` (and `use_effect_with_deps`)
|
||||
All hooks other than `use_effect` (and `use_effect_with`)
|
||||
will function normally until a component successfully renders into `Html`
|
||||
for the first time.
|
||||
|
||||
@ -69,7 +69,7 @@ Web APIs such as `web_sys` are not available when your component is
|
||||
rendering on the server side.
|
||||
Your application will panic if you try to use them.
|
||||
You should isolate logics that need Web APIs in `use_effect` or
|
||||
`use_effect_with_deps` as effects are not executed during server-side rendering.
|
||||
`use_effect_with` as effects are not executed during server-side rendering.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -116,10 +116,10 @@ fn NavButton() -> Html {
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let theme = use_memo(|_| Theme {
|
||||
let theme = use_memo((), |_| Theme {
|
||||
foreground: "yellow".to_owned(),
|
||||
background: "pink".to_owned(),
|
||||
}, ());
|
||||
});
|
||||
|
||||
html! {
|
||||
<ContextProvider<Rc<Theme>> context={theme}>
|
||||
|
||||
@ -59,7 +59,7 @@ where
|
||||
```
|
||||
|
||||
This simple hook can be created by composing built-in hooks. For this example, we'll use the
|
||||
`use_effect_with_deps` hook, so an event listener can be recreated when the hook arguments change.
|
||||
`use_effect_with` hook, so an event listener can be recreated when the hook arguments change.
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
@ -87,7 +87,8 @@ where
|
||||
callback: Callback::from(callback),
|
||||
};
|
||||
|
||||
use_effect_with_deps(
|
||||
use_effect_with(
|
||||
deps,
|
||||
|deps| {
|
||||
let EventDependents {
|
||||
target,
|
||||
@ -103,7 +104,6 @@ where
|
||||
drop(listener);
|
||||
}
|
||||
},
|
||||
deps,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
@ -34,7 +34,7 @@ Yew comes with the following predefined Hooks:
|
||||
- `use_reducer`
|
||||
- `use_reducer_eq`
|
||||
- `use_effect`
|
||||
- `use_effect_with_deps`
|
||||
- `use_effect_with`
|
||||
- `use_context`
|
||||
- `use_force_update`
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ use gloo::utils::document;
|
||||
fn MyComponent() -> Html {
|
||||
// memoize as this only needs to be executed once
|
||||
let node = use_memo(
|
||||
(),
|
||||
|_| {
|
||||
// Create a div element from the document
|
||||
let div: Element = document().create_element("div").unwrap();
|
||||
@ -33,7 +34,6 @@ fn MyComponent() -> Html {
|
||||
// Return that Node as a Html value
|
||||
Html::VRef(node)
|
||||
},
|
||||
(),
|
||||
);
|
||||
|
||||
// use_memo return Rc so we need to deref and clone
|
||||
|
||||
@ -369,7 +369,7 @@ You may want to listen to an event that is not supported by Yew's `html` macro,
|
||||
[supported events listed here](#event-types).
|
||||
|
||||
In order to add an event listener to one of elements manually we need the help of
|
||||
[`NodeRef`](../function-components/node-refs.mdx) so that in `use_effect_with_deps` we can add a listener using the
|
||||
[`NodeRef`](../function-components/node-refs.mdx) so that in `use_effect_with` we can add a listener using the
|
||||
[`web-sys`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/index.html) and
|
||||
[wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html) API.
|
||||
|
||||
@ -394,7 +394,8 @@ use yew::prelude::*;
|
||||
fn MyComponent() -> Html {
|
||||
let div_node_ref = use_node_ref();
|
||||
|
||||
use_effect_with_deps(
|
||||
use_effect_with(
|
||||
div_node_ref.clone(),
|
||||
{
|
||||
let div_node_ref = div_node_ref.clone();
|
||||
|
||||
@ -425,8 +426,7 @@ fn MyComponent() -> Html {
|
||||
|
||||
move || drop(custard_listener)
|
||||
}
|
||||
},
|
||||
div_node_ref.clone()
|
||||
}
|
||||
);
|
||||
|
||||
html! {
|
||||
@ -461,7 +461,8 @@ use gloo::events::EventListener;
|
||||
fn MyComponent() -> Html {
|
||||
let div_node_ref = use_node_ref();
|
||||
|
||||
use_effect_with_deps(
|
||||
use_effect_with(
|
||||
div_node_ref.clone(),
|
||||
{
|
||||
let div_node_ref = div_node_ref.clone();
|
||||
|
||||
@ -486,8 +487,7 @@ fn MyComponent() -> Html {
|
||||
|
||||
move || drop(custard_listener)
|
||||
}
|
||||
},
|
||||
div_node_ref.clone()
|
||||
}
|
||||
);
|
||||
|
||||
html! {
|
||||
|
||||
62
website/docs/migration-guides/yew/from-0_20_0-to-next.mdx
Normal file
62
website/docs/migration-guides/yew/from-0_20_0-to-next.mdx
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
title: 'From 0.19.0 to Next'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## Dependencies as first hook argument and `use_effect_with`
|
||||
|
||||
- Replace `use_effect_with_deps` with new `use_effect_with`
|
||||
- `use_effect_with`, `use_callback`, `use_memo` now take dependencies as their first argument
|
||||
|
||||
### Automated refactor
|
||||
|
||||
With the help of https://crates.io/crates/ast-grep
|
||||
Here are commands that can do the refactoring for you.
|
||||
|
||||
```bash
|
||||
sg --pattern 'use_effect_with_deps($CALLBACK,$$$DEPENDENCIES)' --rewrite 'use_effect_with($$$DEPENDENCIES, $CALLBACK)' -l rs -i
|
||||
sg --pattern 'use_effect_with($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_effect_with($DEPENDENCIES,$$$CALLBACK)' -l rs -i
|
||||
|
||||
sg --pattern 'use_callback($CALLBACK,$$$DEPENDENCIES)' --rewrite 'use_callback($$$DEPENDENCIES, $CALLBACK)' -l rs -i
|
||||
sg --pattern 'use_callback($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_callback($DEPENDENCIES,$$$CALLBACK)' -l rs -i
|
||||
|
||||
sg --pattern 'use_memo($CALLBACK,$$$DEPENDENCIES)' --rewrite 'use_memo($$$DEPENDENCIES, $CALLBACK)' -l rs -i
|
||||
sg --pattern 'use_memo($DEPENDENCIES,,$$$CALLBACK)' --rewrite 'use_memo($DEPENDENCIES,$$$CALLBACK)' -l rs -i
|
||||
```
|
||||
|
||||
### Reasoning
|
||||
|
||||
This will enable more ergonomic use of hooks, consider:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="before" label="Before" default>
|
||||
|
||||
```rust ,ignore
|
||||
impl SomeLargeStruct {
|
||||
fn id(&self) -> u32; // Only need to use the id as cache key
|
||||
}
|
||||
let some_dep: SomeLargeStruct = todo!();
|
||||
|
||||
{
|
||||
let id = some_dep.id(); // Have to extract it in advance, some_dep is moved already in the second argument
|
||||
use_effect_with_dep(move |_| { todo!(); drop(some_dep); }, id);
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="after" label="After">
|
||||
|
||||
```rust ,ignore
|
||||
impl SomeLargeStruct {
|
||||
fn id(&self) -> u32; // Only need to use the id as cache key
|
||||
}
|
||||
let some_dep: SomeLargeStruct = todo!();
|
||||
|
||||
use_effect_with(some_dep.id(), move |_| { todo!(); drop(some_dep); });
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@ -525,7 +525,7 @@ fn app() -> Html {
|
||||
+ let videos = use_state(|| vec![]);
|
||||
+ {
|
||||
+ let videos = videos.clone();
|
||||
+ use_effect_with_deps(move |_| {
|
||||
+ use_effect_with((), move |_| {
|
||||
+ let videos = videos.clone();
|
||||
+ wasm_bindgen_futures::spawn_local(async move {
|
||||
+ let fetched_videos: Vec<Video> = Request::get("https://yew.rs/tutorial/data.json")
|
||||
@ -538,7 +538,7 @@ fn app() -> Html {
|
||||
+ videos.set(fetched_videos);
|
||||
+ });
|
||||
+ || ()
|
||||
+ }, ());
|
||||
+ });
|
||||
+ }
|
||||
|
||||
// ...
|
||||
|
||||
@ -153,17 +153,25 @@ module.exports = {
|
||||
{
|
||||
type: 'category',
|
||||
label: 'yew',
|
||||
items: ['migration-guides/yew/from-0_18_0-to-0_19_0'],
|
||||
items: [
|
||||
'migration-guides/yew/from-0_20_0-to-next',
|
||||
'migration-guides/yew/from-0_19_0-to-0_20_0',
|
||||
'migration-guides/yew/from-0_18_0-to-0_19_0',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'yew-agent',
|
||||
items: ['migration-guides/yew-agent/from-0_0_0-to-0_1_0'],
|
||||
items: [
|
||||
'migration-guides/yew-agent/from-0_1_0-to-0_2_0',
|
||||
'migration-guides/yew-agent/from-0_0_0-to-0_1_0',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'yew-router',
|
||||
items: [
|
||||
'migration-guides/yew-router/from-0_16_0-to-0_17_0',
|
||||
'migration-guides/yew-router/from-0_15_0-to-0_16_0',
|
||||
],
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user