Skip to content
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

Why there are two portion of a Rc<RefCell<T>> data between Callback and Closure? #3240

Closed
3 tasks
Mng12345 opened this issue Apr 20, 2023 · 1 comment
Closed
3 tasks
Labels

Comments

@Mng12345
Copy link

Problem

I have created an util function that make three callbacks for drag an element. One of them is mouse_down which is created by Callback and binded in yew html macro, another two callbacks are created by Closure, this two callbacks are binded into the body element. This three callbacks are using a state named is_draged, so i created a variable is_draged by Rc<RefCell<bool>>, each of this three callbacks moved a copy of is_dragged into their closure context, the variable is_dragged is changed in callback mouse_down and mouse_up. The issue is that the change of is_dragged made by mouse_down is not synced into mouse_up and mouse_move, i printted the data ptr of is_dragged use Rc::as_ptr, the result showed that the ptr in mouse_down is different with mouse_up and mouse_move, but it is the same between mouse_up and mouse_move.
Steps To Reproduce
Steps to reproduce the behavior:

  1. See the minimum reproducable example code below:

The drag utils

mod drag_utils {
   use std::{cell::RefCell, rc::Rc};

   use js_sys::Function;
   use wasm_bindgen::prelude::Closure;
   use yew::{Callback, NodeRef};

   use crate::utils::dom_utils::document;

   #[derive(Debug, Clone)]
   pub struct Callbacks {
       pub mouse_down: Callback<yew::MouseEvent, ()>,
   }

   #[derive(Debug)]
   struct IsDragged {
       pub inner: bool,
       pub id: String,
   }

   pub fn setup_drag<F, F2>(
       on_mouse_move: Rc<F>,
       on_mouse_down: Box<F2>,
       container: NodeRef,
   ) -> Callbacks
   where
       F: Fn(f64) + 'static,
       F2: Fn() + 'static,
   {
       log::info!("called setup_drag");
       let is_dragged = Rc::new(RefCell::new(IsDragged {
           inner: false,
           id: "123".to_string(),
       }));
       let mouse_down = Callback::from({
           let is_dragged = is_dragged.clone();
           move |event: yew::MouseEvent| {
               event.stop_propagation();
               event.prevent_default();
               log::info!(
                   "ptr of is_dragged in mouse_down: {:?}",
                   Rc::as_ptr(&is_dragged)
               );
               match is_dragged.try_borrow_mut() {
                   Ok(mut is_dragged) => {
                       log::info!("is_dragged before mouse down: {:?}", is_dragged);
                       is_dragged.inner = true;
                       is_dragged.id = "1234".to_string();
                       on_mouse_down();
                       log::info!("is_dragged after mouse down: {:?}", is_dragged);
                   }
                   Err(err) => {
                       log::error!("borrow is_dragged failed: {:?}", err)
                   }
               }
           }
       });
       let mouse_up: Closure<dyn Fn(yew::MouseEvent)> = Closure::new({
           let is_dragged = is_dragged.clone();
           move |event: yew::MouseEvent| {
               event.stop_propagation();
               event.prevent_default();
               log::info!(
                   "ptr of is_dragged in mouse_up: {:?}",
                   Rc::as_ptr(&is_dragged)
               );
               match is_dragged.try_borrow_mut() {
                   Ok(mut is_dragged) => {
                       log::info!("is_dragged in mouse_up: {:?}", is_dragged);
                       is_dragged.inner = false;
                       is_dragged.id = "1235".to_string();
                   }
                   Err(err) => {
                       log::error!("borrow is_dragged failed: {:?}", err)
                   }
               }
           }
       });
       let mouse_move: Closure<dyn Fn(yew::MouseEvent)> = Closure::new({
           let container = container.clone();
           let is_dragged = is_dragged.clone();
           move |event: yew::MouseEvent| {
               log::info!(
                   "ptr of is_dragged in mouse_move: {:?}",
                   Rc::as_ptr(&is_dragged)
               );
               match is_dragged.try_borrow() {
                   Ok(is_dragged) => {
                       log::info!("is_dragged in mouse_move: {:?}", is_dragged);

                       if is_dragged.inner {
                           event.stop_propagation();
                           event.prevent_default();
                           match container.cast::<web_sys::HtmlElement>() {
                               None => (),
                               Some(container) => {
                                   let pos = (event.client_x() - container.client_left()) as f64;
                                   on_mouse_move(pos)
                               }
                           }
                       }
                   }
                   Err(err) => {
                       log::error!("borrowed is_dragged failed: {:?}", err)
                   }
               }
           }
       });
       // bind mouse_up into body
       document()
           .body()
           .expect("get body")
           .set_onmouseup(Some(&Function::from(mouse_up.into_js_value())));
       // bind mouse_move into body
       document()
           .body()
           .expect("get body")
           .set_onmousemove(Some(&Function::from(mouse_move.into_js_value())));
       Callbacks { mouse_down }
   }
}

bind mouse_down in html macro

let left_handler_callbacks = yew::use_mut_ref(|| {
        drag_utils::setup_drag(
            Rc::new(|| {}), // for simple 
            left_handler_mouse_down,
            frames_container.clone(),
        )
    });

html! {
  <div onmousedown={left_handler_callbacks.borrow().mouse_down.clone()}>

 </div>
}
  1. Run the code and see the result in console.
![image](https://user-images.githubusercontent.com/21278900/233344896-067ef45f-37e3-402a-9d0f-d1e00e9d8755.png)

Expected behavior
The value of is_dragged should be the same between this three callbacks while it is not.
The ptr (represented the RefCell) should be the same between thie three callback while it is not.

Screenshots
See the step 2.

Environment:

  • Yew version: [e.g. v0.20, master]
  • Rust version: [e.g. 1.67.0, nightly]
  • Target, if relevant: [e.g. wasm32-unknown-emscripten]
  • Build tool, if relevant: [e.g. wasm-pack, trunk]
  • OS, if relevant: [e.g. wsl ubuntu22.04]
  • Browser and version, if relevant: [e.g. Chrome v112]

Questionnaire

  • I'm interested in fixing this myself but don't know where to start
  • I would like to fix and I have a solution
  • I don't have time to fix this right now, but maybe later
@Mng12345 Mng12345 added the bug label Apr 20, 2023
@Mng12345
Copy link
Author

This werid behavior is because i used body.set_on...event, which would cover the listener binded before!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant