From 2b6b2ca9e486175cec4a476d0d2e9a82b974ee32 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 2 Jul 2026 16:43:15 +0800 Subject: [PATCH] =?UTF-8?q?fix(hooks):=20=E4=BF=AE=E5=A4=8D=20wasm32=20tar?= =?UTF-8?q?get=20=E7=BC=96=E8=AF=91=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前验证只跑了 server target(cargo build --all-features),漏掉 wasm32 前端 target,导致三类错误未被捕获: 1. event_listener.rs wasm 分支缺 use dioxus::prelude::*: use_hook/use_effect/use_drop 来自 prelude,之前重写时误删。补回。 2. event_listener.rs use_effect 闭包 move 冲突(E0507/E0310): use_effect 回调是 FnMut,但 acquire 是 FnOnce、handler 要 move 进事件 闭包。用 Option::take 模式包裹,首次运行消费,避免重复 move。同时给 A 加上 'static 约束。 3. query.rs items/total/footer.rs sync_visible 缺 mut: wasm 分支的 .set() 调用需要 mut 绑定。之前为消 server target 的 unused_mut 警告误删,改用 cfg_attr 在非 wasm 时压制警告。 教训:fullstack 项目必须同时验证 wasm32 与 server 两个 target,单一 target 通过不代表整体可用。 --- src/components/footer.rs | 2 +- src/hooks/event_listener.rs | 14 +++++++++++--- src/hooks/query.rs | 5 +++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/footer.rs b/src/components/footer.rs index 1522ba6..c7ea308 100644 --- a/src/components/footer.rs +++ b/src/components/footer.rs @@ -21,7 +21,7 @@ pub fn Footer() -> Element { // 根据 window 当前滚动位置同步 visible(注册监听后立即调用一次,避免首屏漏判)。 // 滚动事件回调里也复用同一份判断逻辑。 - let sync_visible = move || { + let mut sync_visible = move || { #[cfg(target_arch = "wasm32")] { if let Some(w) = web_sys::window() { diff --git a/src/hooks/event_listener.rs b/src/hooks/event_listener.rs index 559fe1e..bf2eceb 100644 --- a/src/hooks/event_listener.rs +++ b/src/hooks/event_listener.rs @@ -30,12 +30,13 @@ /// ); /// ``` #[cfg(target_arch = "wasm32")] -pub fn use_event_listener(acquire: A, event: &'static str, mut handler: F) +pub fn use_event_listener(acquire: A, event: &'static str, handler: F) where T: AsRef + Clone + 'static, - A: FnOnce() -> Option, + A: FnOnce() -> Option + 'static, F: FnMut() + 'static, { + use dioxus::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -45,7 +46,14 @@ where use_hook(|| Rc::new(RefCell::new(None))); let state_for_drop = state.clone(); + // use_effect 的回调是 FnMut(可能多次运行),但 acquire 是 FnOnce、handler 要 + // move 进事件闭包。用 Option 包裹后 take(),首次运行消费掉,避免重复 move。 + let mut acquire = Some(acquire); + let mut handler = Some(handler); + use_effect(move || { + let Some(acquire) = acquire.take() else { return }; + let Some(mut handler) = handler.take() else { return }; let Some(target) = acquire() else { return }; let target_clone = target.clone(); let closure = wasm_bindgen::prelude::Closure::wrap(Box::new(move || { @@ -77,7 +85,7 @@ where pub fn use_event_listener(acquire: A, event: &'static str, handler: F) where T: Clone + 'static, - A: FnOnce() -> Option, + A: FnOnce() -> Option + 'static, F: FnMut() + 'static, { // SSR 无 DOM,空实现。 diff --git a/src/hooks/query.rs b/src/hooks/query.rs index 7e33c06..e4cca0b 100644 --- a/src/hooks/query.rs +++ b/src/hooks/query.rs @@ -46,6 +46,7 @@ pub struct PaginatedState { /// }, /// ); /// ``` +#[cfg_attr(not(target_arch = "wasm32"), allow(unused_mut))] #[allow(unused_variables)] pub fn use_paginated( page: P, @@ -60,8 +61,8 @@ where E: std::fmt::Display + 'static, { let mut loading = use_signal(|| true); - let items = use_signal(Vec::new); - let total = use_signal(|| 0_i64); + let mut items = use_signal(Vec::new); + let mut total = use_signal(|| 0_i64); let mut error: Signal> = use_signal(|| None); use_effect(move || {