chinedufn / percy

Build frontend browser apps with Rust + WebAssembly. Supports server side rendering.
https://chinedufn.github.io/percy/
Apache License 2.0
2.27k stars 84 forks source link

Triggering state changes as part of an on_visit handler #123

Open murtyjones opened 5 years ago

murtyjones commented 5 years ago

Hi!

I'd like to trigger a change to state (in this case, to hide an element) when a page is visited. To do so I put a .msg() in an on_visit handler, like so:

#[route(
  path = "/mypage",
  on_visit = hide_side_nav
)]
fn my_route(store: Provided<Rc<RefCell<Store>>>) -> VirtualNode {
    MyView::new(Rc::clone(&store)).render()
}

fn hide_side_nav(store: Provided<Rc<RefCell<Store>>>) {
    store.borrow_mut().msg(&Msg::HideSideNav);
}

Expected behavior

Page loads and the element I've specified is hidden as a result of the above message being sent

Actual behavior

This runtime error:

panicked at 'already borrowed: BorrowMutError'

Minimal Reproducible Example

In the isomorphic example, you can reproduce by changing download_contributers_json to send a message like so:

#[route(
  path = "/contributors",
  on_visit = download_contributors_json
)]
fn contributors_route(store: Provided<Rc<RefCell<Store>>>) -> VirtualNode {
    ContributorsView::new(Rc::clone(&store)).render()
}

fn download_contributors_json(store: Provided<Rc<RefCell<Store>>>) {
    store.borrow_mut().msg(&Msg::InitiatedContributorsDownload); // Arbitrary message
}

Notes

From reading a little bit about the error is that it's more of an Rc/RefCell problem than a percy problem. I thought would be useful to post it here anyways because I imagine others will get snagged on it at some point.

chinedufn commented 5 years ago

Yeah you're running into this:

https://github.com/chinedufn/percy/blob/e41857e0d002ade865a4c0b9f1c052f75dc06d0b/examples/isomorphic/app/src/lib.rs#L83-L86

Which was recently discovered by @austinsheep


I haven't yet thought about what changes in state management approach could fix this problem - so for now you might just use the same requestAnimationFrame strategy.

Open to any ideas you have of course!

I'll leave this issue open as this is definitely a problem that should be resolved!

chinedufn commented 5 years ago

More context

Basically on_visit gets called while Store is mutably borrowed so you can't just borrow it again.

https://github.com/chinedufn/percy/blob/e41857e0d002ade865a4c0b9f1c052f75dc06d0b/examples/isomorphic/app/src/store.rs#L25-L34

murtyjones commented 5 years ago

Should've read the comments. Makes sense! Thank you

chinedufn commented 5 years ago

If you don't mind - going to keep this open until we land on a better way to handle this scenario which, like you said, other people will surely run into.

Thanks!