You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I think that use_effect_with_deps could fulfil a very useful pattern if Dependents are resolved within the post_render call. This does mean that the hook now needs to use a FnOnce() -> Dependents instead of just Dependents, so bit of ergonomic pain but I think it's worth it!
I came to this when looking to add the use_node_ref hook - I wanted a really useful example of using NodeRef which is set after render which led me to use_effect_with_deps. I thought great I can add my NodeRef to the element and then use it with that hook - I'll add an event listener and then remove it.The problems with this is that if you use the NodeRef as a Dependents then when the destructor is being called the Node is None and you can't clean up 😞.
Edit:
This is wrong 😞 you can use NodeRef as a Dependents and then move the result of a NodeRef::get or NodeRef::cast call to the destructor in order to clean up!
The solution I came up with is to evaluate the Dependents within the post_render, this way calling NodeRef::get or NodeRef::cast to create a Dependents would be okay.
As a side note for this example; use_node_ref works pretty much how you think it might - it creates a NodeRef::default() on it's very first call and persists this reference across renders.
#[function_component(Comp)]pubfncomp() -> Html{let clicked = use_state(|| false);let div_ref = use_node_ref();{// cloned because we want to use clicked in the event listenerlet clicked = clicked.clone();// cloned because we want to move into the `Dependents` fnlet div_ref = div_ref.clone();use_effect_with_deps(
|div:&HtmlElement| {let listener = Closure::<dynFn(Event)>::wrap(Box::new(move |_:Event| {
clicked.set(true);}));
yew::web_sys::console::info_1(&"add listener".into());
div.add_event_listener_with_callback("click", listener.as_ref().unchecked_ref()).unwrap();// need to clone the &HtmlElement to get a HtmlElement for move.let div = div.clone();// we move listener into the closure as it will keep the Box in scope for us// until the destructor is called!move || {
div.remove_event_listener_with_callback("click",
listener.as_ref().unchecked_ref(),).unwrap();
yew::web_sys::console::info_1(&"removed onclick listener".into());}},move || div_ref.cast::<HtmlElement>().expect("div_ref to be set"),);}let text = if*clicked {"Clicked!"}else{"Not clicked"};html!{
<div ref={div_ref}>
<p>{ text }</p>
</div>
}}
This can be used for anything that isn't valid in the view but will be post render like query selectors on the dom. If you always knew an element would exist you could use the HtmlInputElement::value output as a Dependents too for example :)
A nice change but I want to take it a step further and use the old Dependents in the Destructor too, in the previous example this would allow you to omit cloning the div to get an owned version of HtmlElement. In the hook we have the old Dependents saved anyway so this is actually pretty easy to do anyway.
I'm curious what others think and whether there are some issues in this approach - I do have the implementation working so it is possible :)
The example with this final tweak included:
#[function_component(Comp)]pubfncomp() -> Html{let clicked = use_state(|| false);let div_ref = use_node_ref();{let clicked = clicked.clone();let div_ref = div_ref.clone();use_effect_with_deps(
|div:&HtmlElement| {let listener = Closure::<dynFn(Event)>::wrap(Box::new(move |_:Event| {
clicked.set(true);}));
yew::web_sys::console::info_1(&"add listener".into());
div.add_event_listener_with_callback("click", listener.as_ref().unchecked_ref()).unwrap();// we move listener into the closure as it will keep the Box in scope for us// until the destructor is called!move |div| {
div.remove_event_listener_with_callback("click",
listener.as_ref().unchecked_ref(),).unwrap();
yew::web_sys::console::info_1(&"removed onclick listener".into());}},move || div_ref.cast::<HtmlElement>().expect("div_ref to be set"),);}let text = if*clicked {"Clicked!"}else{"Not clicked"};html!{
<div ref={div_ref}>
<p>{ text }</p>
</div>
}}
Edit:
Although my initial thought was this helped aid NodeRef integration with use_effect_with_deps, this isn't really the case (see my correction above). Though it might still be of some interest regarding the ability to use values only accessible at render as Dependents and having access to those old Dependents in the destructor.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
I think that
use_effect_with_deps
could fulfil a very useful pattern ifDependents
are resolved within thepost_render
call. This does mean that the hook now needs to use aFnOnce() -> Dependents
instead of justDependents
, so bit of ergonomic pain but I think it's worth it!I came to this when looking to add the
use_node_ref
hook - I wanted a really useful example of usingNodeRef
which is set after render which led me touse_effect_with_deps
. I thought great I can add myNodeRef
to the element and then use it with that hook - I'll add an event listener and then remove it.The problems with this is that if you use theNodeRef
as aDependents
then when the destructor is being called theNode
isNone
and you can't clean up 😞.Edit:
This is wrong 😞 you can use
NodeRef
as aDependents
and then move the result of aNodeRef::get
orNodeRef::cast
call to the destructor in order to clean up!The solution I came up with is to evaluate the
Dependents
within thepost_render
, this way callingNodeRef::get
orNodeRef::cast
to create aDependents
would be okay.As a side note for this example;
use_node_ref
works pretty much how you think it might - it creates aNodeRef::default()
on it's very first call and persists this reference across renders.This can be used for anything that isn't valid in the
view
but will be post render like query selectors on the dom. If you always knew an element would exist you could use theHtmlInputElement::value
output as aDependents
too for example :)A nice change but I want to take it a step further and use the old
Dependents
in theDestructor
too, in the previous example this would allow you to omit cloning thediv
to get an owned version ofHtmlElement
. In the hook we have the oldDependents
saved anyway so this is actually pretty easy to do anyway.I'm curious what others think and whether there are some issues in this approach - I do have the implementation working so it is possible :)
The example with this final tweak included:
Edit:
Although my initial thought was this helped aid
NodeRef
integration withuse_effect_with_deps
, this isn't really the case (see my correction above). Though it might still be of some interest regarding the ability to use values only accessible at render asDependents
and having access to those oldDependents
in the destructor.Beta Was this translation helpful? Give feedback.
All reactions