refactor: enhance content loading and cancellation with AbortController and improved state reset logic.

This commit is contained in:
2025-11-29 14:27:35 +01:00
parent 79aa359c1f
commit 9cf63ece72
+21 -14
View File
@@ -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) => {