yggdrasil/src/hooks/query.rs
xfy 65a7b1226f style: 全项目格式规范化(cargo fmt)+ Docker daemon 断连容错
- 全项目 .rs 统一 cargo fmt 排版(换行/缩进/参数分行/导入排序)
- infra/docker: DOCKER_CLIENT 连接失败不 panic,降级返回 None
  (博客不依赖 Docker,panic=abort 下会导致整个进程崩溃)
- infra/mod: 去除多余空行
- database/mod: 模块声明按字母序重新排序
2026-07-15 17:22:06 +08:00

97 lines
3.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 通用客户端数据加载 hook。
//!
//! 封装 `use_effect + spawn + 多个 signal` 的三件套,统一 loading / data / error
//! 三态与非 wasm cfg 处理。后台 CSR 页面用这套;公共 SSR 页面仍用
//! `use_server_future`(带 suspend 的真 SSR 预取),二者职责区分明确。
//!
//! 当前只提供 `use_paginated`分页列表。system tab 的"load_once + use_future
//! 轮询"形态、dashboard 的多源聚合形态保持手写——强行套 hook 反而更复杂。
use dioxus::prelude::*;
/// 分页列表加载状态。
///
/// 各字段是独立 `Signal`,调用方可按名读取、传给子组件,或对 `items` 做
/// `with_mut` 乐观更新。
pub struct PaginatedState<T> {
/// 是否正在加载。
pub loading: Signal<bool>,
/// 当前页数据。
pub items: Signal<Vec<T>>,
/// 总条数(用于分页器)。
pub total: Signal<i64>,
/// 最近一次加载错误(成功时为 None
pub error: Signal<Option<String>>,
}
/// 分页列表加载 hook。
///
/// - `page`:返回当前页码的信号读取闭包(返回值变化时自动重新加载)。
/// - `per_page`:每页条数。
/// - `fetch`:接收 `(page, per_page)`,返回 `Future<Output = Result<(Vec<T>, i64), E>>`。
/// `E: Display` 用于把错误写入 `error` signal。
///
/// 非 wasm32 下 `loading` 立即置 falseSSR 不发起请求)。
///
/// # 例
///
/// ```ignore
/// let state = use_paginated(
/// || current_page,
/// POSTS_PER_PAGE,
/// |p, pp| async move {
/// list_posts(p, pp).await
/// .map(|r| (r.posts, r.total))
/// .map_err(|e| e.to_string())
/// },
/// );
/// ```
#[cfg_attr(not(target_arch = "wasm32"), allow(unused_mut))]
#[allow(unused_variables)]
pub fn use_paginated<T, P, F, Fut, E>(page: P, per_page: i32, fetch: F) -> PaginatedState<T>
where
T: PartialEq + Clone + 'static,
P: Fn() -> i32 + Copy + 'static,
F: Fn(i32, i32) -> Fut + Copy + 'static,
Fut: std::future::Future<Output = Result<(Vec<T>, i64), E>> + 'static,
E: std::fmt::Display + 'static,
{
let mut loading = use_signal(|| true);
let mut items = use_signal(Vec::new);
let mut total = use_signal(|| 0_i64);
let mut error: Signal<Option<String>> = use_signal(|| None);
use_effect(move || {
let p = page();
loading.set(true);
error.set(None);
#[cfg(target_arch = "wasm32")]
{
let f = fetch;
spawn(async move {
match f(p, per_page).await {
Ok((list, t)) => {
items.set(list);
total.set(t);
}
Err(e) => error.set(Some(e.to_string())),
}
loading.set(false);
});
}
#[cfg(not(target_arch = "wasm32"))]
{
loading.set(false);
// 抚平非 wasm 下未使用的变量p / per_page / fetch
let _ = (p, per_page, fetch);
}
});
PaginatedState {
loading,
items,
total,
error,
}
}