-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add set_if_neq for UseStateHandle #2109
Conversation
Visit the preview URL for this PR (updated for commit c63c5df): https://yew-rs--pr2109-master-7ptlqx8t.web.app (expires Fri, 22 Oct 2021 00:30:59 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just want to expand on the docs a little bit more.
Last call for bike shedding the name :) @yoroshikun, @WorldSEnder, @hamza1311 (thought I'd ping you too as you were part of the discussion on Discord)
It has a 👍 from me - thank you for driving this forward @voidpumpkin :)
Bikeshedding input:
|
@WorldSEnder Loved the suggestions and implemented them both |
I think this implementation might not work reliably if the handle is moved into a callback / event listener. (This might not be a good example, but is one I have on hand.) use web_sys::window;
use yew::prelude::*;
use yew_router::attach_route_listener;
use yew_router::prelude::*;
#[derive(Routable, Debug, Clone, PartialEq)]
pub enum Route {
#[at("/")]
Home,
#[at("/a")]
A,
#[at("/b")]
B,
}
#[function_component(Content)]
fn content() -> Html {
let pathname = use_state(|| window().unwrap().location().pathname().unwrap());
let route = use_state(Route::current_route);
let pathname_clone = pathname.clone();
let route_clone = route.clone();
use_state(move || {
attach_route_listener(Callback::from(move |r: Option<Route>| {
route_clone.set_if_neq(r);
pathname_clone.set_if_neq(window().unwrap().location().pathname().unwrap());
}))
});
html! {
<>
<div>{"Listener: "}{format!("{:?}", *route)}</div>
<div>{"Pathname: "}{pathname.to_string()}</div>
<Link<Route> route={Route::Home}>{"Home"}</Link<Route>>
<Link<Route> route={Route::A}>{"A"}</Link<Route>>
<Link<Route> route={Route::B}>{"B"}</Link<Route>>
</>
}
}
#[function_component(App)]
fn app() -> Html {
let render_route = Router::render(|_| {
html! {<Content />}
});
html! {<Router<Route> render={render_route} />}
}
fn main() {
yew::start_app::<App>();
} In the example above, the component will not rerender if the new value is You can replicate this by clicking: Home -> A -> Home |
It does not, this is to do with how This is an issue that either needs resolving or documenting but I think it should become its own issue as this PR doesn't cause the issue and any resolution would equally apply to |
I think const [height, setHeight] = useState(0);
useEffect(() => {
// height can become stale but setHeight still works.
window.addEventListener("resize", () => setHeight(window.innerHeight));
}, []); I think this behaviour can be achieved by implementing |
The issue with doing the check in the callback is that you haven't solved the whole problem - if a user prefers to perform an equality check manually before using At present this PR acts in the same manner as the manual approach: // Manual equality check
if *state != new_state {
state.set(new_state);
}
// new
state.set_if_neq(new_state); Is it perfect behaviour, no, but it is consistent with the current semantics of Not letting this PR through because of this issue actually doesn't change the fact that the manual approach still has the behaviour you described. I would like to see some resolution to #2112 before the next release though - I want it to work with router listener and this also effects bridge callbacks too. |
I don't know if this is how you prefer to do it in yew, but i would expect to treat agents like event listeners. Here is react example https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1 |
@futursolo Here is how I would solve your problem: Essentially you need to keep Here is the solution to your specific problem: https://github.com/voidpumpkin/yew-playground/tree/stale-agents @mc1098 I don't think #2112 is even a real issue since in react world it works exactly like that. PS: |
Agent dies when all bridges to it are dropped. If the current bridge is the only bridge to an agent, every time this is destroyed / created, it will cause the agent to be repeatedly created / destroyed at the same time. In addition, for private and job agents, each time a new bridge is created, it creates a new agent, which means any previous state in the agent is lost when re-bridged (this also applies to public and context if current bridge is the only bridge).
In React, if you need to register an event listener that only uses the setter of a state, all of the following ways work: useEffect(cb, []);
// probably recommended approach, but setter of a state never changes anyways.
useEffect(cb, [setValue]);
// or
useEffect(cb); |
I somehow missed that my fix did not fix anything :D @futursolo |
Still i don't think this separate issue blocks this PR Or you think a solution for #2112 would create a world where |
Agreed.
I see I agree #2112 needs to be resolved somehow but shouldn't block this PR because that wouldn't stop the bug from existing or really change how and when you'd hit this bug (it just might make it more convenient to do so). |
@futursolo Sorry for the spam.. But in the end I did solve the problem by applying two fixes: https://github.com/voidpumpkin/yew-playground/tree/stale-agents First applied fix
Second applied fixstale setter gets countered by simply creating a new event listener on every route state change GifOther commentsI noticed that new router does not use an agent, it just creates an event I still think there are cases where stale setters create problems, would be good to get a small reproducible case in #2112 cc @mc1098 (Again sorry for spam) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is just a question whether a bool return would be useful :)
Apart from that I'm happy with this PR.
* add set_neq for UseStateHandle * add docs * update based on PR comments 1 (cherry picked from commit f2a0d61)
Description
Added a
set_neq
method forUseStateHandle
that will only cause a re render when value is different than already setFixes #2104
Checklist
cargo make pr-flow