From 0e4eccec211b165d40523d039b8eeb4ffdac2b1e Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:53:13 +0800 Subject: [PATCH] =?UTF-8?q?chore(lesson-preparation):=20V4=20lint=20+=20ts?= =?UTF-8?q?c=20=E9=9B=B6=E9=94=99=E8=AF=AF=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构 anchor-migration-banner:使用 key-based remount + lazy initial state 避免 setState in effect,符合 V4 react-hooks/set-state-in-effect 规则。 验证:tsc --noEmit 0 错误;lint 中 V4 引入的错误已全部修复。剩余 6 个 lint 错误为预存无关模块问题(exams/use-exam-preview-tasks react-hooks/refs 3 个、template-picker V5-9 set-state-in-effect 1 个),13 个 warnings 均为预存代码(schedule-dialog、unread-message-badge 等),按计划要求已有的无关模块错误需记录但不要求修复。 --- .../components/anchor-migration-banner.tsx | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/modules/lesson-preparation/components/anchor-migration-banner.tsx b/src/modules/lesson-preparation/components/anchor-migration-banner.tsx index cd798d9..ec110d2 100644 --- a/src/modules/lesson-preparation/components/anchor-migration-banner.tsx +++ b/src/modules/lesson-preparation/components/anchor-migration-banner.tsx @@ -1,28 +1,44 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState } from "react"; import { useTranslations } from "next-intl"; import { useLessonPlanEditor } from "../hooks/use-lesson-plan-editor"; +import type { LessonPlanDocument } from "../types"; /** * V4:v3 锚点失效提示 banner。 * 当文档含旧锚点(invalid: true 或含 start/end 字段)且未 dismiss 时显示。 * dismiss 状态记录到 localStorage(按 planId)。 + * + * 通过 key={planId} 实现 planId 变化时重新挂载内部组件, + * 从而使用 lazy initial state 读取 localStorage,避免在 effect 中 setState。 */ export function AnchorMigrationBanner() { - const t = useTranslations("lessonPreparation"); const doc = useLessonPlanEditor((s) => s.doc); const planId = useLessonPlanEditor((s) => s.planId); - const [dismissed, setDismissed] = useState(false); - useEffect(() => { - if (planId) { - const stored = localStorage.getItem(`lp-legacy-anchor-dismissed-${planId}`); - setDismissed(stored === "true"); - } else { - setDismissed(false); - } - }, [planId]); + return ( + + ); +} + +function BannerContent({ + doc, + planId, +}: { + doc: LessonPlanDocument; + planId: string; +}) { + const t = useTranslations("lessonPreparation"); + const [dismissed, setDismissed] = useState(() => { + if (typeof window === "undefined") return false; + if (!planId) return false; + return localStorage.getItem(`lp-legacy-anchor-dismissed-${planId}`) === "true"; + }); // 检测是否有旧锚点(invalid: true 或含 start/end 字段) const hasLegacyAnchors = doc.anchors.some(