fix: #1789 标题字号滑块拖动回弹 — useMemo 稳定引用 + ref 防御 (#1793)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 3s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 18s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 20s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m20s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 1m26s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 1m42s
CI/CD Pipeline / Validate - Style (push) Successful in 2m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 52s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m27s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m30s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m10s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m14s
CI/CD Pipeline / Validate - Security (push) Successful in 6m17s
CI/CD Pipeline / Unit Tests (push) Failing after 8m32s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped

This commit was merged in pull request #1793.
This commit is contained in:
2026-09-08 13:28:50 +08:00
parent 9dec75c365
commit 6904fce511
2 changed files with 19 additions and 7 deletions
@@ -1,4 +1,4 @@
import { useState, useEffect, useRef, useCallback } from "react"
import { useState, useEffect, useRef, useCallback, useMemo } from "react"
import { useQuery } from "@tanstack/react-query"
import { message } from "antd"
import { getEditingTemplates } from "@/api/editing-planner"
@@ -22,8 +22,10 @@ export function useTemplateSelection() {
})
// 双保险:后端 valid_only 已过滤,前端再按 is_active + segments 兜底,
// 保证下拉/自动选择只包含可用于生成的有效模板
const validTemplates = allTemplates.filter(isValidTemplate)
// 保证下拉/自动选择只包含可用于生成的有效模板
// 用 useMemo 缓存引用,避免每次渲染都 .filter 创建新数组,
// 导致下游 useTitleCoverSync effect 无限触发、覆盖用户手动修改(#1789)
const validTemplates = useMemo(() => allTemplates.filter(isValidTemplate), [allTemplates])
const userTemplates = validTemplates
// 用 ref 持有最新值,供稳定回调 handleInvalidTemplate 使用(避免闭包拿到旧值)
@@ -1,4 +1,4 @@
import { useEffect } from "react"
import { useEffect, useRef } from "react"
import type { TitleSettings } from "../../types"
import type { CoverConfig } from "../../types/cover"
import type { EditingTemplate } from "@/api/editing-planner"
@@ -11,7 +11,11 @@ interface UseTitleCoverSyncOptions {
}
/**
* 当选中模板变化时,自动同步标题和封面配置
* 当选中模板变化时,自动同步标题和封面配置
*
* #1789 修复:userTemplates 用 ref 持有最新值,不放入依赖数组。
* 否则每次渲染 .filter() 创建的新数组引用都会触发 effect,
* 从模板 title_config 覆盖用户手动修改(如字号滑块拖动),导致回弹。
*/
export function useTitleCoverSync({
selectedTemplate,
@@ -19,8 +23,12 @@ export function useTitleCoverSync({
setTitleSettings,
setCoverSettings,
}: UseTitleCoverSyncOptions) {
// 用 ref 持有最新 userTemplates,避免数组引用变化导致 effect 反复触发
const templatesRef = useRef(userTemplates)
templatesRef.current = userTemplates
useEffect(() => {
const tpl = userTemplates.find((t) => t.id === selectedTemplate)
const tpl = templatesRef.current.find((t) => t.id === selectedTemplate)
if (tpl?.title_config) {
setTitleSettings((prev: TitleSettings) => ({
...prev,
@@ -43,5 +51,7 @@ export function useTitleCoverSync({
thumbnail_url: tpl.cover_config!.thumbnail_url || prev.thumbnail_url,
}))
}
}, [selectedTemplate, userTemplates, setTitleSettings, setCoverSettings])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedTemplate, setTitleSettings, setCoverSettings])
// ↑ 移除 userTemplates,只在 selectedTemplate 真正变化时触发
}