mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat: Improve GitHubIssuesView with stable event handler references
- Introduced refs for selected issue and validation dialog state to prevent unnecessary re-subscribing on state changes. - Added cleanup logic to ensure proper handling of asynchronous operations during component unmounting. - Enhanced error handling in validation loading functions to only log errors if the component is still mounted, improving reliability.
This commit is contained in:
@@ -64,6 +64,9 @@ export function GitHubIssuesView() {
|
|||||||
// Track revalidation confirmation dialog
|
// Track revalidation confirmation dialog
|
||||||
const [showRevalidateConfirm, setShowRevalidateConfirm] = useState(false);
|
const [showRevalidateConfirm, setShowRevalidateConfirm] = useState(false);
|
||||||
const audioRef = useRef<HTMLAudioElement | null>(null);
|
const audioRef = useRef<HTMLAudioElement | null>(null);
|
||||||
|
// Refs for stable event handler (avoids re-subscribing on state changes)
|
||||||
|
const selectedIssueRef = useRef<GitHubIssue | null>(null);
|
||||||
|
const showValidationDialogRef = useRef(false);
|
||||||
const {
|
const {
|
||||||
currentProject,
|
currentProject,
|
||||||
validationModel,
|
validationModel,
|
||||||
@@ -129,6 +132,8 @@ export function GitHubIssuesView() {
|
|||||||
|
|
||||||
// Load cached validations on mount
|
// Load cached validations on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let isMounted = true;
|
||||||
|
|
||||||
const loadCachedValidations = async () => {
|
const loadCachedValidations = async () => {
|
||||||
if (!currentProject?.path) return;
|
if (!currentProject?.path) return;
|
||||||
|
|
||||||
@@ -136,7 +141,7 @@ export function GitHubIssuesView() {
|
|||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (api.github?.getValidations) {
|
if (api.github?.getValidations) {
|
||||||
const result = await api.github.getValidations(currentProject.path);
|
const result = await api.github.getValidations(currentProject.path);
|
||||||
if (result.success && result.validations) {
|
if (isMounted && result.success && result.validations) {
|
||||||
const map = new Map<number, StoredValidation>();
|
const map = new Map<number, StoredValidation>();
|
||||||
for (const v of result.validations) {
|
for (const v of result.validations) {
|
||||||
map.set(v.issueNumber, v);
|
map.set(v.issueNumber, v);
|
||||||
@@ -145,15 +150,23 @@ export function GitHubIssuesView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[GitHubIssuesView] Failed to load cached validations:', err);
|
if (isMounted) {
|
||||||
|
console.error('[GitHubIssuesView] Failed to load cached validations:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadCachedValidations();
|
loadCachedValidations();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
};
|
||||||
}, [currentProject?.path]);
|
}, [currentProject?.path]);
|
||||||
|
|
||||||
// Load running validations on mount (restore validatingIssues state)
|
// Load running validations on mount (restore validatingIssues state)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let isMounted = true;
|
||||||
|
|
||||||
const loadRunningValidations = async () => {
|
const loadRunningValidations = async () => {
|
||||||
if (!currentProject?.path) return;
|
if (!currentProject?.path) return;
|
||||||
|
|
||||||
@@ -161,18 +174,33 @@ export function GitHubIssuesView() {
|
|||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (api.github?.getValidationStatus) {
|
if (api.github?.getValidationStatus) {
|
||||||
const result = await api.github.getValidationStatus(currentProject.path);
|
const result = await api.github.getValidationStatus(currentProject.path);
|
||||||
if (result.success && result.runningIssues) {
|
if (isMounted && result.success && result.runningIssues) {
|
||||||
setValidatingIssues(new Set(result.runningIssues));
|
setValidatingIssues(new Set(result.runningIssues));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[GitHubIssuesView] Failed to load running validations:', err);
|
if (isMounted) {
|
||||||
|
console.error('[GitHubIssuesView] Failed to load running validations:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadRunningValidations();
|
loadRunningValidations();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
};
|
||||||
}, [currentProject?.path]);
|
}, [currentProject?.path]);
|
||||||
|
|
||||||
|
// Keep refs in sync with state for stable event handler
|
||||||
|
useEffect(() => {
|
||||||
|
selectedIssueRef.current = selectedIssue;
|
||||||
|
}, [selectedIssue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
showValidationDialogRef.current = showValidationDialog;
|
||||||
|
}, [showValidationDialog]);
|
||||||
|
|
||||||
// Subscribe to validation events
|
// Subscribe to validation events
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
@@ -232,7 +260,10 @@ export function GitHubIssuesView() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If validation dialog is open for this issue, update the result
|
// If validation dialog is open for this issue, update the result
|
||||||
if (selectedIssue?.number === event.issueNumber && showValidationDialog) {
|
if (
|
||||||
|
selectedIssueRef.current?.number === event.issueNumber &&
|
||||||
|
showValidationDialogRef.current
|
||||||
|
) {
|
||||||
setValidationResult(event.result);
|
setValidationResult(event.result);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -246,7 +277,10 @@ export function GitHubIssuesView() {
|
|||||||
toast.error(`Validation failed for issue #${event.issueNumber}`, {
|
toast.error(`Validation failed for issue #${event.issueNumber}`, {
|
||||||
description: event.error,
|
description: event.error,
|
||||||
});
|
});
|
||||||
if (selectedIssue?.number === event.issueNumber && showValidationDialog) {
|
if (
|
||||||
|
selectedIssueRef.current?.number === event.issueNumber &&
|
||||||
|
showValidationDialogRef.current
|
||||||
|
) {
|
||||||
setShowValidationDialog(false);
|
setShowValidationDialog(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -255,7 +289,7 @@ export function GitHubIssuesView() {
|
|||||||
|
|
||||||
const unsubscribe = api.github.onValidationEvent(handleValidationEvent);
|
const unsubscribe = api.github.onValidationEvent(handleValidationEvent);
|
||||||
return () => unsubscribe();
|
return () => unsubscribe();
|
||||||
}, [currentProject?.path, selectedIssue, showValidationDialog, muteDoneSound]);
|
}, [currentProject?.path, muteDoneSound]);
|
||||||
|
|
||||||
// Cleanup audio element on unmount to prevent memory leaks
|
// Cleanup audio element on unmount to prevent memory leaks
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user