You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Yew的use_effect_with_deps监听器中设置状态?

在Yew的use_effect_with_deps监听器中设置状态的解决方案

我在use_effect内添加了一个鼠标移动事件监听器,想要获取鼠标坐标并更新组件状态。使用use_node_ref绑定元素添加监听器时,出现了如下生命周期错误:

error[E0597]: `coordinates` does not live long enough
  --> src/test.rs:33:17
   |
24 |           |div_ref| {
   |           --------- value captured here
...
29 |               let listener = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|event| {
   |  ________________________________________________________________-
30 | |                 let x = event.x();
31 | |                 let y = event.y();
32 | |                 web_sys::console::log_1(&x.into());
33 | |                 coordinates.set(Coordinates { x: x, y: y });
   | |                 ^^^^^^^^^^^ borrowed value does not live long enough
34 | |                 
35 | |             }));
   | |______________- cast requires that `coordinates` is borrowed for `'static`
...
53 |   }
   |   - `coordinates` dropped here while still borrowed

可复现示例代码:

use wasm_bindgen::prelude::Closure;
use web_sys::HtmlElement;
use yew::prelude::*;
use wasm_bindgen::JsCast;
use web_sys;
use yew::{function_component, html, use_effect_with_deps, use_node_ref, Html};

struct Coordinates {
    x: i32,
    y: i32,
}

#[function_component]
fn Test() -> Html {
    let div_ref = use_node_ref();
    let coordinates: UseStateHandle<Coordinates> = use_state(|| Coordinates { x: 0, y: 0 });
        

{
    let div_ref = div_ref.clone();
    let coordinates = coordinates.clone();

    use_effect_with_deps(
        |div_ref| {
            let div = div_ref
                .cast::<HtmlElement>()
                .expect("div_ref not attached to div element");

            let listener = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|event| {
                let x = event.x();
                let y = event.y();
                web_sys::console::log_1(&x.into());
                coordinates.set(Coordinates { x: x, y: y }); // <- here is the problem
                
            }));

            div.add_event_listener_with_callback(
                "mousemove",
                listener.as_ref().unchecked_ref(),
            )
            .unwrap();

            move || {
                div.remove_event_listener_with_callback(
                    "mousemove",
                    listener.as_ref().unchecked_ref(),
                )
                .unwrap();
            }
        },
        div_ref,
    );
}

    html! {
        <div ref={div_ref}>"Test"</div>
    }
}

fn main() {
    yew::Renderer::<Test>::new().render();
}

问题原因

错误的核心是Closure::wrap要求闭包捕获的变量必须拥有'static生命周期,但UseStateHandle的克隆实例在use_effect_with_deps的作用域内无法自动满足这个要求——闭包需要长期存在(直到组件卸载或依赖更新),但原作用域的变量会在use_effect_with_deps执行完毕后被销毁,导致生命周期不匹配。

修复方案

通过move闭包转移UseStateHandle的所有权,同时确保闭包能安全满足'static要求,修改后的代码如下:

use wasm_bindgen::prelude::Closure;
use web_sys::HtmlElement;
use yew::prelude::*;
use wasm_bindgen::JsCast;
use web_sys;
use yew::{function_component, html, use_effect_with_deps, use_node_ref, Html};

struct Coordinates {
    x: i32,
    y: i32,
}

#[function_component]
fn Test() -> Html {
    let div_ref = use_node_ref();
    let coordinates: UseStateHandle<Coordinates> = use_state(|| Coordinates { x: 0, y: 0 });
        

{
    let div_ref = div_ref.clone();
    let coordinates = coordinates.clone();

    use_effect_with_deps(
        |div_ref| {
            let div = div_ref
                .cast::<HtmlElement>()
                .expect("div_ref not attached to div element");
            
            // 使用move闭包转移coordinates的所有权,满足'static要求
            let listener = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(move |event| {
                let x = event.x();
                let y = event.y();
                web_sys::console::log_1(&x.into());
                coordinates.set(Coordinates { x, y });
            }));

            div.add_event_listener_with_callback(
                "mousemove",
                listener.as_ref().unchecked_ref(),
            )
            .unwrap();

            // 将listener移动到清理闭包中,确保移除监听器时能正确引用
            move || {
                div.remove_event_listener_with_callback(
                    "mousemove",
                    listener.as_ref().unchecked_ref(),
                )
                .unwrap();
            }
        },
        div_ref,
    );
}

    html! {
        <div ref={div_ref}>"Test"</div>
    }
}

fn main() {
    yew::Renderer::<Test>::new().render();
}

关键修改说明

  1. 使用move闭包:在创建事件监听器的闭包时添加move关键字,将coordinates的所有权转移到闭包内部,这样闭包就能满足'static生命周期要求——UseStateHandle本身是基于Rc的智能指针,克隆和移动不会影响原实例的有效性。
  2. 正确处理listener的生命周期:将listener移动到清理闭包中,确保在移除事件监听器时,闭包实例仍然存在,避免悬垂引用。

内容的提问来源于stack exchange,提问作者programandoconro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:57:52