1- import React , { useEffect , useState } from 'react' ;
1+ import React , { useEffect , useRef , useState } from 'react' ;
22import BasicInfoView from './BasicInfoView' ;
33import AltNamesTab from './tabs/AltNamesTab' ;
44import AddressesTab from './tabs/AddressesTab' ;
@@ -68,36 +68,40 @@ export default function TabContentLoader({
6868 onBasicInfoEditorStateChange,
6969 onRegisterBasicInfoSaveHandler,
7070} : Props ) {
71- const [ cacheState , setCacheState ] = useState < { personId : number | null ; tabs : Record < string , TabState > } > ( {
72- personId,
73- tabs : { } ,
74- } ) ;
71+ const [ cache , setCache ] = useState < Record < string , TabState > > ( { } ) ;
72+ const [ fetchSeq , setFetchSeq ] = useState ( 0 ) ;
73+ const cachePersonRef = useRef < number | null > ( personId ) ;
7574
76- // 人物切換時重設快取;本次渲染直接使用空快取,不等下一輪
77- const personChanged = cacheState . personId !== personId ;
75+ // 人物切換時同步清快取,本次渲染直接使用空值
76+ const personChanged = cachePersonRef . current !== personId ;
7877 if ( personChanged ) {
79- setCacheState ( { personId, tabs : { } } ) ;
78+ cachePersonRef . current = personId ;
79+ if ( Object . keys ( cache ) . length > 0 ) {
80+ setCache ( { } ) ;
81+ }
8082 }
8183
82- const cache = personChanged ? { } : cacheState . tabs ;
83- const setCache = ( updater : Record < string , TabState > | ( ( prev : Record < string , TabState > ) => Record < string , TabState > ) ) => {
84- setCacheState ( ( prev ) => ( {
85- ...prev ,
86- tabs : typeof updater === 'function' ? updater ( prev . tabs ) : updater ,
87- } ) ) ;
88- } ;
84+ const effectiveCache = personChanged ? { } : cache ;
8985
90- // lazy load
86+ // lazy load — 由 personId / activeTab / fetchSeq 驅動
9187 useEffect ( ( ) => {
9288 if ( personId == null || ! activeTab ) return ;
93- if ( cache [ activeTab ] ) return ;
89+
90+ // 透過 updater 讀取最新 cache,避免把 cache 引用放進依賴
91+ let alreadyCached = false ;
92+ setCache ( ( prev ) => {
93+ if ( prev [ activeTab ] ) {
94+ alreadyCached = true ;
95+ return prev ;
96+ }
97+ return { ...prev , [ activeTab ] : { loading : true , error : null , data : null } } ;
98+ } ) ;
99+ if ( alreadyCached ) return ;
94100
95101 const url = tabEndpoint
96102 . replace ( '__PERSON_ID__' , String ( personId ) )
97103 . replace ( '__TAB_KEY__' , activeTab ) ;
98104
99- setCache ( ( prev ) => ( { ...prev , [ activeTab ] : { loading : true , error : null , data : null } } ) ) ;
100-
101105 const controller = new AbortController ( ) ;
102106
103107 fetch ( url , { signal : controller . signal } )
@@ -124,22 +128,22 @@ export default function TabContentLoader({
124128 return ( ) => {
125129 controller . abort ( ) ;
126130 } ;
127- } , [ personId , activeTab , tabEndpoint , cache ] ) ;
131+ } , [ personId , activeTab , tabEndpoint , fetchSeq ] ) ;
128132
129133 const retryActiveTab = ( ) => {
130134 setCache ( ( prev ) => {
131135 const next = { ...prev } ;
132136 delete next [ activeTab ] ;
133-
134137 return next ;
135138 } ) ;
139+ setFetchSeq ( ( s ) => s + 1 ) ;
136140 } ;
137141
138142 if ( personId == null ) {
139143 return null ;
140144 }
141145
142- const state = cache [ activeTab ] ;
146+ const state = effectiveCache [ activeTab ] ;
143147
144148 if ( ! state || state . loading ) {
145149 return < div style = { msgStyle } > 載入中…</ div > ;
0 commit comments