From 93ad1ac04a52e015501d458c58e1c62c4d1a9817 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Wed, 19 Aug 2026 17:42:31 +0100 Subject: [PATCH] refactor(webapp): simplify manual memoization --- .../app/components/billing/BillingAlertsSection.tsx | 10 +++++++--- .../components/dashboard-agent/DashboardAgentPanel.tsx | 7 ++++--- apps/webapp/app/hooks/useDashboardEditor.ts | 2 ++ .../route.tsx | 5 +---- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/apps/webapp/app/components/billing/BillingAlertsSection.tsx b/apps/webapp/app/components/billing/BillingAlertsSection.tsx index ce2f5e5e0fc..ed95af59238 100644 --- a/apps/webapp/app/components/billing/BillingAlertsSection.tsx +++ b/apps/webapp/app/components/billing/BillingAlertsSection.tsx @@ -140,14 +140,18 @@ export function BillingAlertsSection({ ); const maxAlerts = isPercentageMode ? MAX_PERCENTAGE_ALERTS : MAX_ABSOLUTE_ALERTS; + /* oxlint-disable react/react-compiler -- Stable derived thresholds prevent the synchronization effect from resetting local edits. */ const savedThresholds = useMemo( () => storedAlertsToThresholds(alerts, billingLimitMode, effectiveLimitCents, planLimitCents), [alerts, billingLimitMode, effectiveLimitCents, planLimitCents] ); + /* oxlint-enable react/react-compiler */ const savedEmails = useMemo(() => alerts.emails, [alerts.emails]); - const hasLegacySpikes = useMemo( - () => hasLegacySpikeAlertLevels(alerts, billingLimitMode, effectiveLimitCents, planLimitCents), - [alerts, billingLimitMode, effectiveLimitCents, planLimitCents] + const hasLegacySpikes = hasLegacySpikeAlertLevels( + alerts, + billingLimitMode, + effectiveLimitCents, + planLimitCents ); const nextThresholdIdRef = useRef(savedThresholds.length); diff --git a/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx b/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx index 93a9064b4ce..598167f260c 100644 --- a/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx +++ b/apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx @@ -416,6 +416,7 @@ export function DashboardAgentPanel({ }, []); const dismissWatchCard = () => dispatchWatchCard({ type: "dismissed" }); + const activeChatId = active?.chatId; const submitWatch = useCallback(async () => { const draft = watchCard.draft; @@ -429,7 +430,7 @@ export function DashboardAgentPanel({ body.set("draft", JSON.stringify(draft)); body.set("clientRequestId", clientRequestId); // A watch is chat-bound: with no chat open the server creates one. - if (active?.chatId) body.set("chatId", active.chatId); + if (activeChatId) body.set("chatId", activeChatId); const res = await fetch(actionPath, { method: "POST", body }); const data = (await res.json()) as { @@ -446,7 +447,7 @@ export function DashboardAgentPanel({ } const messages = data.messages; - if (active?.chatId === data.chatId) { + if (activeChatId === data.chatId) { setAppendedMessages((current) => ({ chatId: data.chatId!, messages, @@ -474,7 +475,7 @@ export function DashboardAgentPanel({ }, [ watchCard.draft, watchCard.requestId, - active?.chatId, + activeChatId, actionPath, organization.id, claimChatSlot, diff --git a/apps/webapp/app/hooks/useDashboardEditor.ts b/apps/webapp/app/hooks/useDashboardEditor.ts index 30a0bae3e44..53fba85c35b 100644 --- a/apps/webapp/app/hooks/useDashboardEditor.ts +++ b/apps/webapp/app/hooks/useDashboardEditor.ts @@ -257,6 +257,7 @@ export function useDashboardEditor({ // Sync queue processor - ensures only one sync runs at a time // ------------------------------------------------------------------------- + /* oxlint-disable react/react-compiler -- The recursive callback drains a serialized sync queue. */ const processNextSync = useCallback(async () => { // If already syncing or queue is empty, do nothing if (isSyncingRef.current || syncQueueRef.current.length === 0) { @@ -309,6 +310,7 @@ export function useDashboardEditor({ processNextSync(); } }, [widgetActionUrl, layoutActionUrl, onSyncError]); + /* oxlint-enable react/react-compiler */ // ------------------------------------------------------------------------- // Queue helpers diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx index 6afeb1a6e6b..e6e77e279d6 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx @@ -1430,10 +1430,7 @@ function QueueEnvMetricChart({ [tile.id, tile.label, lineColor] ); - const { tickFormatter, tooltipLabelFormatter } = useMemo( - () => buildActivityTimeAxis(data), - [data] - ); + const { tickFormatter, tooltipLabelFormatter } = buildActivityTimeAxis(data); const hasData = data.length > 0 && data.some((p) => Number(p[tile.id] ?? 0) > 0); // Peak readout lives in the card title (ChartCard has no dedicated value slot). A zero/empty