Merge pull request #697 from buster-so/devin/1754746916-fix-memory-leaks

Fix critical memory leaks in Next.js navigation
This commit is contained in:
Nate Kelley 2025-08-09 22:41:50 -06:00 committed by GitHub
commit 978749230e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 22 deletions

View File

@ -149,15 +149,14 @@ export const useTooltipOptions = ({
}, [selectedAxis, selectedChartType, disableTooltip, columnLabelFormats, columnSettings]);
useUnmount(() => {
const tooltipEl = document.getElementById('buster-chartjs-tooltip');
if (tooltipEl) {
// Remove all event listeners before removing the element
const clonedEl = tooltipEl.cloneNode(true) as HTMLElement;
tooltipEl.parentNode?.replaceChild(clonedEl, tooltipEl);
clonedEl.remove();
}
// Clear the cache to prevent memory leaks
tooltipCache.current = {};
// Remove tooltip element if it exists
const tooltipEl = document.getElementById('buster-chartjs-tooltip');
if (tooltipEl && tooltipEl.parentNode) {
tooltipEl.parentNode.removeChild(tooltipEl);
}
});
return tooltipOptions;

View File

@ -122,12 +122,14 @@ export const PreventNavigation: React.FC<PreventNavigationProps> = React.memo(
});
useEffect(() => {
/* *************************** Open listeners ************************** */
for (const link of document.querySelectorAll('a')) {
link.addEventListener('click', handleClick);
if (isDirty) {
/* *************************** Open listeners ************************** */
for (const link of document.querySelectorAll('a')) {
link.addEventListener('click', handleClick);
}
window.addEventListener('popstate', handlePopState);
window.addEventListener('beforeunload', handleBeforeUnload);
}
window.addEventListener('popstate', handlePopState);
window.addEventListener('beforeunload', handleBeforeUnload);
/* ************** Return from useEffect closing listeners ************** */
return () => {
@ -137,7 +139,7 @@ export const PreventNavigation: React.FC<PreventNavigationProps> = React.memo(
window.removeEventListener('popstate', handlePopState);
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [isDirty]);
}, [isDirty, handleClick, handlePopState, handleBeforeUnload]);
const onClose = useMemoizedFn(async () => {
setLeavingPage(false);

View File

@ -36,17 +36,19 @@ export const getTextWidth = (
return 0;
}
// Set the font style for the text
context.font = `${fontSize}px ${fontFamily}`;
try {
// Set the font style for the text
context.font = `${fontSize}px ${fontFamily}`;
// Measure the width of the string
const width = context.measureText(text).width;
// Measure the width of the string
const width = context.measureText(text).width;
// Clean up the temporary canvas
context.clearRect(0, 0, canvas.width, canvas.height);
canvas.remove();
return width;
return width;
} finally {
canvas.width = 0;
canvas.height = 0;
canvas.remove();
}
};
export const getBase64 = (file: File): Promise<string> => {

View File

@ -135,6 +135,12 @@ const DotPattern = ({ children, isDark }: DotPatternProps) => {
ctx.fill();
}
}
return () => {
if (ctx && canvas) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
};
}, [isDark]);
return (