99import { useEffect , useCallback , useRef , useMemo , useState } from 'react' ;
1010import { useNavigate , useParams } from 'react-router-dom' ;
1111import { EditorContent } from '@tiptap/react' ;
12- import { useShallow } from 'zustand/react/shallow' ;
1312import { useNoteStore } from '../store/useNoteStore' ;
1413import { CornellLayout } from '../components/CornellLayout' ;
1514import FreeCanvas from '../components/FreeCanvas' ;
@@ -60,7 +59,12 @@ export default function NoteEditPage() {
6059 } ,
6160 } ) ;
6261
63- const { notes, updateNote, selectNote, loadNotes } = useNoteStore ( useShallow ( s => s ) ) ;
62+ // 细粒度 selector:整 store 订阅(useShallow(s => s))会在任何字段变化时
63+ // 重渲染整页,键入场景下与 healthText 叠加放大卡顿
64+ const notes = useNoteStore ( ( s ) => s . notes ) ;
65+ const updateNote = useNoteStore ( ( s ) => s . updateNote ) ;
66+ const selectNote = useNoteStore ( ( s ) => s . selectNote ) ;
67+ const loadNotes = useNoteStore ( ( s ) => s . loadNotes ) ;
6468 const note = notes . find ( ( n ) => n . id === noteId ) || null ;
6569
6670 const titleRef = useRef < HTMLInputElement > ( null ) ;
@@ -82,13 +86,27 @@ export default function NoteEditPage() {
8286 const [ tierOpen , setTierOpen ] = useState ( false ) ;
8387
8488 // === N3 笔记健康度:跟踪编辑器实时文本供工具栏指示器计算 ===
89+ // 流畅度修复:原版每次键入同步 setHealthText 导致 457 行整页每键重渲染。
90+ // 现改为 1s 防抖 + 打开消费面板时惰性取最新快照,键入路径不再触发整页重渲染
8591 const [ healthText , setHealthText ] = useState ( '' ) ;
8692 useEffect ( ( ) => {
8793 if ( ! editor ) return ;
8894 setHealthText ( editor . getText ( ) ) ;
89- const onHealthUpdate = ( ) => setHealthText ( editor . getText ( ) ) ;
95+ let timer : ReturnType < typeof setTimeout > | null = null ;
96+ const onHealthUpdate = ( ) => {
97+ if ( timer ) clearTimeout ( timer ) ;
98+ timer = setTimeout ( ( ) => setHealthText ( editor . getText ( ) ) , 1000 ) ;
99+ } ;
90100 editor . on ( 'update' , onHealthUpdate ) ;
91- return ( ) => { editor . off ( 'update' , onHealthUpdate ) ; } ;
101+ return ( ) => {
102+ editor . off ( 'update' , onHealthUpdate ) ;
103+ if ( timer ) clearTimeout ( timer ) ;
104+ } ;
105+ } , [ editor ] ) ;
106+
107+ /** 惰性快照:打开消费 healthText 的面板前取编辑器最新文本,避免防抖窗口内数据滞后 */
108+ const refreshHealthText = useCallback ( ( ) => {
109+ if ( editor ) setHealthText ( editor . getText ( ) ) ;
92110 } , [ editor ] ) ;
93111
94112 // === N6 概念冲突检测:内容稳定后自动比对新旧理解 ===
@@ -244,6 +262,18 @@ export default function NoteEditPage() {
244262 soundPlayer . play ( 'note_manual_save' ) ;
245263 } ;
246264
265+ // 合书测试入口:先同步最新文本快照再打开(healthText 为防抖值,可能滞后≤ 1s)
266+ const handleOpenClosedBook = useCallback ( ( ) => {
267+ refreshHealthText ( ) ;
268+ setClosedBook ( true ) ;
269+ } , [ refreshHealthText ] ) ;
270+
271+ // 内容分层入口:同理先取最新快照
272+ const handleOpenTier = useCallback ( ( ) => {
273+ refreshHealthText ( ) ;
274+ setTierOpen ( true ) ;
275+ } , [ refreshHealthText ] ) ;
276+
247277 // 阶段四:导出当前笔记为 Markdown(导图笔记降级为大纲)
248278 const handleExportMarkdown = ( ) => {
249279 if ( ! note ) return ;
@@ -364,7 +394,7 @@ export default function NoteEditPage() {
364394 < div className = "absolute right-4 top-4 z-10 flex flex-col gap-2" >
365395 < Tip text = "合书测试:隐藏笔记,凭回忆自测" side = "left" >
366396 < button
367- onClick = { ( ) => setClosedBook ( true ) }
397+ onClick = { handleOpenClosedBook }
368398 className = "p-2 rounded-full bg-bg-primary/80 backdrop-blur-sm border border-border/40 text-text-tertiary hover:text-brand-600 hover:border-brand-300 shadow-sm transition-all duration-200"
369399 >
370400 < EyeOff className = "w-4 h-4" strokeWidth = { 1.5 } />
@@ -373,7 +403,7 @@ export default function NoteEditPage() {
373403 { /* N5 内容分层入口:策略性遗忘标记 */ }
374404 < Tip text = "内容分层:聚焦核心概念" side = "left" >
375405 < button
376- onClick = { ( ) => setTierOpen ( true ) }
406+ onClick = { handleOpenTier }
377407 className = "p-2 rounded-full bg-bg-primary/80 backdrop-blur-sm border border-border/40 text-text-tertiary hover:text-brand-600 hover:border-brand-300 shadow-sm transition-all duration-200"
378408 >
379409 < Layers className = "w-4 h-4" strokeWidth = { 1.5 } />
0 commit comments