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(