refactor(ui): enhance chart destruction and instance management

Improve robustness of chart updates in dashboard components by adding
force destroy checks across canvas properties, component state, and
Chart.js registry to prevent memory leaks and instance conflicts.
Save new chart instances to both canvas and component for better tracking.
This commit is contained in:
jgor20
2026-01-11 15:12:41 +00:00
parent fc35b71c2b
commit a76c364921

View File

@@ -135,16 +135,6 @@ window.DashboardCharts.createDataset = function (label, data, color, canvas) {
* @param {object} component - Dashboard component instance * @param {object} component - Dashboard component instance
*/ */
window.DashboardCharts.updateCharts = function (component) { window.DashboardCharts.updateCharts = function (component) {
// Safely destroy existing chart instance FIRST
if (component.charts.quotaDistribution) {
try {
component.charts.quotaDistribution.destroy();
} catch (e) {
console.error("Failed to destroy quota chart:", e);
}
component.charts.quotaDistribution = null;
}
const canvas = document.getElementById("quotaChart"); const canvas = document.getElementById("quotaChart");
// Safety checks // Safety checks
@@ -152,6 +142,33 @@ window.DashboardCharts.updateCharts = function (component) {
console.debug("quotaChart canvas not found"); console.debug("quotaChart canvas not found");
return; return;
} }
// FORCE DESTROY: Check for existing chart on the canvas element property
// This handles cases where Component state is lost but DOM persists
if (canvas._chartInstance) {
console.debug("Destroying existing quota chart from canvas property");
try {
canvas._chartInstance.destroy();
} catch(e) { console.warn(e); }
canvas._chartInstance = null;
}
// Also check component state as backup
if (component.charts.quotaDistribution) {
try {
component.charts.quotaDistribution.destroy();
} catch(e) { }
component.charts.quotaDistribution = null;
}
// Also try Chart.js registry
if (typeof Chart !== "undefined" && Chart.getChart) {
const regChart = Chart.getChart(canvas);
if (regChart) {
try { regChart.destroy(); } catch(e) {}
}
}
if (typeof Chart === "undefined") { if (typeof Chart === "undefined") {
console.warn("Chart.js not loaded"); console.warn("Chart.js not loaded");
return; return;
@@ -250,8 +267,10 @@ window.DashboardCharts.updateCharts = function (component) {
labels.push(depletedLabel); labels.push(depletedLabel);
}); });
// Create Chart
try { try {
component.charts.quotaDistribution = new Chart(canvas, { const newChart = new Chart(canvas, {
// ... config
type: "doughnut", type: "doughnut",
data: { data: {
labels: labels, labels: labels,
@@ -283,6 +302,11 @@ window.DashboardCharts.updateCharts = function (component) {
}, },
}, },
}); });
// SAVE INSTANCE TO CANVAS AND COMPONENT
canvas._chartInstance = newChart;
component.charts.quotaDistribution = newChart;
} catch (e) { } catch (e) {
console.error("Failed to create quota chart:", e); console.error("Failed to create quota chart:", e);
} }
@@ -302,28 +326,46 @@ window.DashboardCharts.updateTrendChart = function (component) {
console.log("[updateTrendChart] Starting update..."); console.log("[updateTrendChart] Starting update...");
// Safely destroy existing chart instance FIRST const canvas = document.getElementById("usageTrendChart");
if (component.charts.usageTrend) {
console.log("[updateTrendChart] Destroying existing chart"); // FORCE DESTROY: Check for existing chart on the canvas element property
if (canvas) {
if (canvas._chartInstance) {
console.debug("Destroying existing trend chart from canvas property");
try { try {
// Stop all animations before destroying to prevent null context errors canvas._chartInstance.stop();
component.charts.usageTrend.stop(); canvas._chartInstance.destroy();
component.charts.usageTrend.destroy(); } catch(e) { console.warn(e); }
} catch (e) { canvas._chartInstance = null;
console.error("[updateTrendChart] Failed to destroy chart:", e);
}
component.charts.usageTrend = null;
} }
const canvas = document.getElementById("usageTrendChart"); // Also try Chart.js registry
if (typeof Chart !== "undefined" && Chart.getChart) {
const regChart = Chart.getChart(canvas);
if (regChart) {
try { regChart.stop(); regChart.destroy(); } catch(e) {}
}
}
}
// Also check component state
if (component.charts.usageTrend) {
try {
component.charts.usageTrend.stop();
component.charts.usageTrend.destroy();
} catch (e) { }
component.charts.usageTrend = null;
}
// Safety checks // Safety checks
if (!canvas) { if (!canvas) {
console.error("[updateTrendChart] Canvas not found in DOM!"); console.error("[updateTrendChart] Canvas not found in DOM!");
_trendChartUpdateLock = false; // Release lock!
return; return;
} }
if (typeof Chart === "undefined") { if (typeof Chart === "undefined") {
console.error("[updateTrendChart] Chart.js not loaded"); console.error("[updateTrendChart] Chart.js not loaded");
_trendChartUpdateLock = false; // Release lock!
return; return;
} }
@@ -476,7 +518,7 @@ window.DashboardCharts.updateTrendChart = function (component) {
} }
try { try {
component.charts.usageTrend = new Chart(canvas, { const newChart = new Chart(canvas, {
type: "line", type: "line",
data: { labels, datasets }, data: { labels, datasets },
options: { options: {
@@ -533,6 +575,11 @@ window.DashboardCharts.updateTrendChart = function (component) {
}, },
}, },
}); });
// SAVE INSTANCE
canvas._chartInstance = newChart;
component.charts.usageTrend = newChart;
} catch (e) { } catch (e) {
console.error("Failed to create trend chart:", e); console.error("Failed to create trend chart:", e);
} finally { } finally {