fix(home,tags): 同变体路由分页/切标签后列表不更新
延续上一个 commit(fix(post))发现的同类 bug,统一修复 home 和 tags: - home: /page/1 → /page/2(同 Route::HomePage 变体)后文章列表不更新 - tags: /tags/a → /tags/b(同 Route::TagDetail 变体)后标签下文章不更新 根因完全一致:use_server_future 闭包内读取的 current_page / tag 是普通 prop (i32 / String),move 进闭包后成为冻结快照,不建立反应式订阅,同变体导航 复用组件实例时 future 不重跑。 注意 / → /page/2(Route::Home → Route::HomePage,跨变体)不受影响,因为跨 变体会新挂载组件、future 首次运行;只有同变体间的 prop 变化才触发 bug。 修复:闭包内通过 router().current::<Route>() 读取当前 page/tag 建立订阅。 Home(/ 路由,Route::Home 变体无 page 字段)调用 HomePosts 时走兜底分支用 传入的 current_page。 验证(headless chromium,本地设 APP_BASE_URL 绕过 CSRF 403): - home 修复前:/page/1 点下一页,URL 变 /page/2,首篇仍是 page1 内容(复现) - home 修复后:首篇随分页变为 page2 内容 - tags 因数据库无多文章标签,无法端到端验证;代码模式与 home 一致且编译通过
This commit is contained in:
parent
79978aa4a4
commit
1c56fd8ec1
@ -51,10 +51,26 @@ pub fn HomePage(page: i32) -> Element {
|
|||||||
///
|
///
|
||||||
/// 通过 `use_server_future` 异步获取当前页文章;
|
/// 通过 `use_server_future` 异步获取当前页文章;
|
||||||
/// 加载中显示骨架屏,加载失败显示错误提示,成功则渲染文章卡片与分页。
|
/// 加载中显示骨架屏,加载失败显示错误提示,成功则渲染文章卡片与分页。
|
||||||
|
///
|
||||||
|
/// # 反应式取数
|
||||||
|
/// 同 `post_detail.rs`:`current_page` 是普通 `i32` prop,被 `move` 进
|
||||||
|
/// `use_server_future` 闭包后成为冻结快照,读取它不建立反应式订阅。因此
|
||||||
|
/// `/page/1 → /page/2`(同为 `Route::HomePage` 变体,复用组件实例)这样的
|
||||||
|
/// 同变体分页导航无法触发 future 重跑。修复方式:在闭包内通过
|
||||||
|
/// `router().current::<Route>()` 读取当前 page,建立订阅,路由变化即重跑。
|
||||||
|
/// `Home`(`/`)调用时路由变体无 page 字段,兜底用传入的 `current_page`。
|
||||||
#[component]
|
#[component]
|
||||||
fn HomePosts(current_page: i32) -> Element {
|
fn HomePosts(current_page: i32) -> Element {
|
||||||
// 调用 server function 获取已发布文章分页数据。
|
let router = dioxus::router::router();
|
||||||
let posts_res = use_server_future(move || list_published_posts(current_page, POSTS_PER_PAGE))?;
|
|
||||||
|
let posts_res = use_server_future(move || {
|
||||||
|
let page = match router.current::<Route>() {
|
||||||
|
Route::HomePage { page } => page.max(1),
|
||||||
|
// / 路由(Route::Home)及其它变体:用 prop 兜底。
|
||||||
|
_ => current_page,
|
||||||
|
};
|
||||||
|
list_published_posts(page, POSTS_PER_PAGE)
|
||||||
|
})?;
|
||||||
|
|
||||||
// 将结果映射为更便于本地使用的 (posts, total) 形式。
|
// 将结果映射为更便于本地使用的 (posts, total) 形式。
|
||||||
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
||||||
|
|||||||
@ -120,9 +120,24 @@ pub fn TagDetail(tag: String) -> Element {
|
|||||||
///
|
///
|
||||||
/// 通过 `use_server_future` 调用 `get_posts_by_tag` 获取指定标签下的文章;
|
/// 通过 `use_server_future` 调用 `get_posts_by_tag` 获取指定标签下的文章;
|
||||||
/// 成功时渲染文章总数与文章卡片。
|
/// 成功时渲染文章总数与文章卡片。
|
||||||
|
///
|
||||||
|
/// # 反应式取数
|
||||||
|
/// 同 `post_detail.rs` / `home.rs`:`tag` 是普通 `String` prop,被 `move` 进
|
||||||
|
/// `use_server_future` 闭包后成为冻结快照,不建立反应式订阅。从标签云点击
|
||||||
|
/// 切换标签(`/tags/a → /tags/b`,同为 `Route::TagDetail` 变体,复用组件实例)
|
||||||
|
/// 时 future 不会重跑。修复:在闭包内通过 `router().current::<Route>()` 读取
|
||||||
|
/// 当前 tag 建立订阅,路由变化即重跑。
|
||||||
#[component]
|
#[component]
|
||||||
fn TagDetailContent(tag: String) -> Element {
|
fn TagDetailContent(tag: String) -> Element {
|
||||||
let posts_res = use_server_future(move || get_posts_by_tag(tag.clone(), None, None))?;
|
let router = dioxus::router::router();
|
||||||
|
|
||||||
|
let posts_res = use_server_future(move || {
|
||||||
|
let current_tag = match router.current::<Route>() {
|
||||||
|
Route::TagDetail { tag } => tag,
|
||||||
|
_ => tag.clone(),
|
||||||
|
};
|
||||||
|
get_posts_by_tag(current_tag, None, None)
|
||||||
|
})?;
|
||||||
|
|
||||||
// 将结果映射为 (posts, total) 形式以便渲染。
|
// 将结果映射为 (posts, total) 形式以便渲染。
|
||||||
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
let posts_data = posts_res.read().as_ref().map(|r| match r {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user