refactor: enhance content loading and cancellation with AbortController and improved state reset logic.
This commit is contained in:
@@ -333,13 +333,16 @@ const DocumentInfoPanel: React.FC<DocumentInfoPanelProps> = ({
|
||||
// Track previous ID/key to avoid unnecessary resets on prop reference changes
|
||||
const prevDocIdRef = React.useRef(document?.id);
|
||||
const prevResetKeyRef = React.useRef(resetKey);
|
||||
const activeControllerRef = React.useRef<AbortController | null>(null);
|
||||
|
||||
// Reset content state when document changes
|
||||
// Reset content state when document changes or contentConfig becomes available
|
||||
useEffect(() => {
|
||||
const docIdChanged = prevDocIdRef.current !== document?.id;
|
||||
const resetKeyChanged = prevResetKeyRef.current !== resetKey;
|
||||
// Check if we need to initialize state (e.g. contentConfig was loaded asynchronously)
|
||||
const needsInit = contentConfig && !contentState;
|
||||
|
||||
if (docIdChanged || resetKeyChanged) {
|
||||
if (docIdChanged || resetKeyChanged || needsInit) {
|
||||
prevDocIdRef.current = document?.id;
|
||||
prevResetKeyRef.current = resetKey;
|
||||
|
||||
@@ -361,29 +364,38 @@ const DocumentInfoPanel: React.FC<DocumentInfoPanelProps> = ({
|
||||
loadContent,
|
||||
document?.id,
|
||||
resetKey,
|
||||
contentState,
|
||||
]);
|
||||
|
||||
// Lazy load content when tab is active
|
||||
// Cleanup effect: aborts when inputs change or component unmounts
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
activeControllerRef.current?.abort();
|
||||
contentConfig?.onCancel?.();
|
||||
};
|
||||
}, [activeTabId, contentConfig, loadContent, contentEnabled, document?.id]);
|
||||
|
||||
// Loading effect: triggers load when status is idle
|
||||
useEffect(() => {
|
||||
const contentTabId = contentConfig?.id || 'content';
|
||||
const isActive = activeTabId === contentTabId;
|
||||
|
||||
if (!isActive || !contentConfig || !loadContent || !contentEnabled) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (contentState?.status !== 'idle') {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
const controller = new AbortController();
|
||||
activeControllerRef.current = controller;
|
||||
|
||||
setContentState({ status: 'loading', data: null, error: null });
|
||||
|
||||
Promise.resolve(loadContent({ signal: controller.signal }))
|
||||
.then((result) => {
|
||||
if (cancelled) {
|
||||
if (controller.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
if (result && result.length) {
|
||||
@@ -393,7 +405,7 @@ const DocumentInfoPanel: React.FC<DocumentInfoPanelProps> = ({
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (cancelled || error?.name === 'AbortError') {
|
||||
if (controller.signal.aborted || error?.name === 'AbortError') {
|
||||
return;
|
||||
}
|
||||
setContentState({
|
||||
@@ -402,18 +414,13 @@ const DocumentInfoPanel: React.FC<DocumentInfoPanelProps> = ({
|
||||
error,
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
controller.abort();
|
||||
contentConfig.onCancel?.();
|
||||
};
|
||||
}, [
|
||||
activeTabId,
|
||||
contentConfig,
|
||||
loadContent,
|
||||
contentEnabled,
|
||||
contentState?.status,
|
||||
document?.id,
|
||||
]);
|
||||
|
||||
const handleTabSelect = (tabId) => {
|
||||
|
||||
Reference in New Issue
Block a user