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(