chore(webapp): scope component effect synchronization (#4727)

## Summary

Scopes React Compiler diagnostics for component and hook effects that
intentionally synchronize with navigation, submissions, browser APIs,
streams, timers, or authoritative server values. Each suppression stays
on the reported synchronization call rather than disabling analysis for
the component.
This commit is contained in:
Chris Arderne
2026-08-20 09:59:43 +01:00
committed by GitHub
parent 101883c41c
commit 7673c46a02
41 changed files with 73 additions and 0 deletions
+1
View File
@@ -273,6 +273,7 @@ function ChatMessages({
// Reset feedback state when conversation is reset
useEffect(() => {
if (conversation.length === 0) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setFeedbackGivenForQAs(new Set());
}
}, [conversation.length]);
@@ -55,6 +55,7 @@ export function DevPresenceProvider({ children, enabled = true }: DevPresencePro
useEffect(() => {
// If disabled or no events
if (!enabled || streamedEvents === null) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsConnected(undefined);
return;
}
@@ -113,6 +114,7 @@ export function useCrossEngineIsConnected({
useEffect(() => {
if (project.engine === "V2") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setCrossEngineIsConnected(isConnected);
return;
}
+1
View File
@@ -76,6 +76,7 @@ export function Feedback({
useEffect(() => {
const open = searchParams.get("feedbackPanel");
if (open) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setType(open as FeedbackType);
setOpen(true);
// Clone instead of mutating in place
@@ -47,6 +47,7 @@ export function LoginPageLayout({
const [randomQuote, setRandomQuote] = useState<QuoteType | null>(null);
useEffect(() => {
const randomIndex = Math.floor(Math.random() * quotes.length);
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setRandomQuote(quotes[randomIndex]);
}, []);
@@ -25,6 +25,7 @@ export function TriggerRotatingLogo() {
useEffect(() => {
// Already registered from a previous render
if (customElements.get("spline-viewer")) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsSplineReady(true);
return;
}
@@ -67,6 +67,7 @@ export function FeatureFlagsDialog({
useEffect(() => {
if (open && orgId) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSaveError(null);
setOverrides({});
setInitialOverrides({});
@@ -77,6 +78,7 @@ export function FeatureFlagsDialog({
useEffect(() => {
if (loadFetcher.data) {
const loaded = loadFetcher.data.orgFlags ?? {};
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOverrides({ ...loaded });
setInitialOverrides({ ...loaded });
}
@@ -86,6 +88,7 @@ export function FeatureFlagsDialog({
if (saveFetcher.data?.success) {
onOpenChangeRef.current(false);
} else if (saveFetcher.data?.error) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSaveError(saveFetcher.data.error);
}
}, [saveFetcher.data]);
@@ -34,10 +34,12 @@ export function MaxProjectsSection({
const [value, setValue] = useState(String(maximumProjectCount));
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
if (hasFieldErrors) setIsEditing(true);
}, [hasFieldErrors]);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
if (savedJustNow && !hasFieldErrors) setIsEditing(false);
}, [savedJustNow, hasFieldErrors]);
@@ -65,10 +65,12 @@ export function RateLimitSection({
const [maxTokens, setMaxTokens] = useState(current ? String(current.maxTokens) : "");
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
if (hasFieldErrors) setIsEditing(true);
}, [hasFieldErrors]);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
if (savedJustNow && !hasFieldErrors) setIsEditing(false);
}, [savedJustNow, hasFieldErrors]);
@@ -119,6 +119,7 @@ export function BillingAlertsSection({
return;
}
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setShowResetBanner(true);
if (searchParams.get("alertsReset") !== "1") {
@@ -189,6 +190,7 @@ export function BillingAlertsSection({
useEffect(() => {
nextThresholdIdRef.current = savedThresholds.length;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setThresholdRows(toThresholdRows(savedThresholds));
setEmailValues(savedEmails.length > 0 ? [...savedEmails, ""] : [""]);
}, [savedThresholds, savedEmails]);
@@ -126,6 +126,7 @@ export function BillingLimitConfigSection({
const formRef = useRef<HTMLFormElement>(null);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMode(resetMode);
setCustomAmount(savedCustomAmount);
setCancelInProgressRuns(savedCancelInProgressRuns);
@@ -61,6 +61,7 @@ export function AIQueryInput({
// If mode is edit but there's no current query, switch to new
useEffect(() => {
if (mode === "edit" && !canEdit) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMode("new");
}
}, [mode, canEdit]);
@@ -93,6 +93,7 @@ export function AgentChart({
// The block can render before `query` has streamed in; an empty query 400s.
if (!block.query) return;
if (!organizationId || !projectId || !environmentId) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState({ status: "error", error: "No environment context to run the query." });
return;
}
@@ -168,6 +168,7 @@ export function DashboardAgentPanel({
// Ordering-safe: if the new chat has not reported yet, its own report re-sets the marker.
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setThinkingChatId((previous) => markerAfterActiveChat(previous, active?.chatId));
}, [active?.chatId]);
@@ -313,6 +314,7 @@ export function DashboardAgentPanel({
void loadHistory();
const stored = readLastChat(storageKey);
if (stored && stored.path === location.pathname) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
void openChat(stored.chatId);
} else {
setLoading(false);
@@ -341,6 +343,7 @@ export function DashboardAgentPanel({
handledOpenChatSeq.current = openChatRequest.seq;
// Reloading the visible transcript would drop a turn in flight.
if (openChatRequest.chatId === active?.chatId) return;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
void openChat(openChatRequest.chatId);
// `active` is read, not tracked: a later change must not re-run the request.
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -357,6 +360,7 @@ export function DashboardAgentPanel({
onChatRead?.(chatId, { leaving: false });
visibleChatId.current = nextVisibleChat(chatId, { leaving: false });
justRead.current.add(chatId);
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setChats((previous) => markChatListRead(previous, chatId));
// Read again on the way out: a wake can land while the chat is open.
return () => {
@@ -384,6 +388,7 @@ export function DashboardAgentPanel({
if (target === "hold") return;
handledRequestSeq.current = requestedMessage.seq;
if (target === "new-chat") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
void createChat(requestedMessage.text);
return;
}
@@ -183,6 +183,7 @@ export function VercelOnboardingModal({
hasSyncedStagingRef.current = false;
hasSyncedPreviewRef.current = false;
} else if (isOpen && state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState(computeInitialState());
}
prevIsOpenRef.current = isOpen;
@@ -262,6 +263,7 @@ export function VercelOnboardingModal({
// Strip "stg" from build settings when the staging environment mapping is cleared
useEffect(() => {
if (!vercelStagingEnvironment) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setPullEnvVarsBeforeBuild((prev) => prev.filter((s) => s !== "stg"));
setDiscoverEnvVars((prev) => prev.filter((s) => s !== "stg"));
}
@@ -329,6 +331,7 @@ export function VercelOnboardingModal({
useEffect(() => {
if (!isOpen) {
hasTriggeredMarketplaceRedirectRef.current = false;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsRedirecting(false);
}
}, [isOpen]);
@@ -390,6 +393,7 @@ export function VercelOnboardingModal({
state === "loading-projects" &&
onboardingData?.availableProjects !== undefined
) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("project-selection");
}
}, [state, onboardingData?.availableProjects, onboardingData?.authInvalid]);
@@ -400,6 +404,7 @@ export function VercelOnboardingModal({
state === "loading-env-vars" &&
onboardingData?.environmentVariables
) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("env-var-sync");
}
}, [state, onboardingData?.environmentVariables, onboardingData?.authInvalid]);
@@ -415,6 +420,7 @@ export function VercelOnboardingModal({
trackOnboarding("vercel onboarding project selected", {
vercel_project_name: selectedVercelProject?.name,
});
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("loading-env-mapping");
if (onDataReload) {
onDataReload();
@@ -437,6 +443,7 @@ export function VercelOnboardingModal({
const hasCustomEnvs =
(onboardingData.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment;
if (hasCustomEnvs && !fromMarketplaceContext) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("env-mapping");
} else {
setState("loading-env-vars");
@@ -661,6 +668,7 @@ export function VercelOnboardingModal({
}
return;
}
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("completed");
}
}, [completeOnboardingFetcher.data, completeOnboardingFetcher.state, state]);
@@ -675,6 +683,7 @@ export function VercelOnboardingModal({
return;
}
}
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("completed");
}
}, [state, isGitHubConnectedForOnboarding, fromMarketplaceContext, nextUrl, trackOnboarding]);
@@ -704,6 +713,7 @@ export function VercelOnboardingModal({
envMappingFetcher.data.success &&
envMappingFetcher.state === "idle"
) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("loading-env-vars");
}
}, [envMappingFetcher.data, envMappingFetcher.state]);
@@ -719,12 +729,14 @@ export function VercelOnboardingModal({
selectedEnv = stagingEnv ?? customEnvironments[0];
}
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setVercelStagingEnvironment({ environmentId: selectedEnv.id, displayName: selectedEnv.slug });
}
}, [state, customEnvironments, vercelStagingEnvironment]);
useEffect(() => {
if (state === "project-selection" && availableProjects.length > 0 && !selectedVercelProject) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSelectedVercelProject(availableProjects[0]);
}
}, [state, availableProjects, selectedVercelProject]);
@@ -68,6 +68,7 @@ export function LogDetailView({ logId, initialLog, onClose, searchTerm }: LogDet
useEffect(() => {
if (!logId) return;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setError(null);
fetcher.load(
`/resources/orgs/${organization.slug}/projects/${project.slug}/env/${
@@ -80,6 +81,7 @@ export function LogDetailView({ logId, initialLog, onClose, searchTerm }: LogDet
// Handle fetch errors
useEffect(() => {
if (fetcher.data && typeof fetcher.data === "object" && "error" in fetcher.data) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setError(fetcher.data.error as string);
} else if (fetcher.state === "idle" && fetcher.data === null && !initialLog) {
setError("Failed to load log details");
@@ -76,6 +76,7 @@ export function LogsTable({
// Show load more spinner only after 0.2 seconds of loading time
useEffect(() => {
if (!isLoadingMore) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setShowLoadMoreSpinner(false);
return;
}
@@ -88,6 +88,7 @@ export function SaveToDashboardDialog({
useEffect(() => {
if (customDashboards.length > 0 && !selectedDashboardId) {
const available = customDashboards.find((d) => d.widgetCount < widgetLimit);
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSelectedDashboardId(available?.friendlyId ?? customDashboards[0].friendlyId);
}
}, [customDashboards, selectedDashboardId, widgetLimit]);
@@ -52,6 +52,7 @@ function useCreateDashboard({
useEffect(() => {
if (navigation.formAction === formAction && navigation.state === "loading") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}
}, [navigation.formAction, navigation.state, formAction]);
@@ -61,6 +61,7 @@ export function EnvironmentSelector({
const revalidator = useRevalidator();
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -249,6 +250,7 @@ function Branches({
}, []);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -49,6 +49,7 @@ export function FavoriteMenuItem({
// Watch search too: navigating to a favorite can change only the search on the same pathname
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMenuOpen(false);
}, [navigation.location?.pathname, navigation.location?.search]);
@@ -438,6 +438,7 @@ export function SideMenu({
const data = customizationFetcher.data;
if (!data) {
// Settled with no response body (e.g. a session-expiry redirect): fail rather than spin
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setCustomizeConfirmPending(false);
setCustomizeError("Couldn't save your changes. Please try again.");
return;
@@ -1488,6 +1489,7 @@ function SideMenuMoreItem({
// Watch search too: navigating to a favorite can change only the search on the same pathname
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOpen(false);
}, [navigation.location?.pathname, navigation.location?.search]);
@@ -1701,6 +1703,7 @@ function OrgSelector({
const planTitle = currentPlan?.v3Subscription?.plan?.title;
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOrgMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -1979,6 +1982,7 @@ function AccountMenu({ isAdmin, isImpersonating }: { isAdmin: boolean; isImperso
const navigation = useNavigation();
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}, [navigation.location?.pathname]);
@@ -2033,6 +2037,7 @@ function ProjectSelector({
const { urlForProject } = usePageSwitcher();
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -2154,6 +2159,7 @@ function SideMenuPopoverSubMenu({
// Close the submenu on navigation (the parent popover closes too).
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}, [navigation.location?.pathname]);
@@ -19,6 +19,7 @@ export function SideMenuHeader({
const navigation = useNavigation();
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setHeaderMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -41,12 +41,14 @@ export function AnimatedCallout({
useEffect(() => {
if (!show) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setAutoDismissed(false);
}
}, [show]);
useEffect(() => {
if (shouldShow) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setRendered(true);
return;
}
@@ -261,6 +261,7 @@ export function ButtonContent(props: ButtonContentPropsType) {
const [showSpinner, setShowSpinner] = useState(false);
useEffect(() => {
if (!isLoading) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setShowSpinner(false);
return;
}
@@ -111,6 +111,7 @@ export const CheckboxWithLabel = React.forwardRef<HTMLInputElement, CheckboxProp
}, [isChecked]);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsChecked(defaultChecked ?? false);
}, [defaultChecked]);
@@ -120,6 +120,7 @@ export function ClipboardField({
const { container, input, buttonVariant, button, size } = variants[variant];
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsSecure(secure !== undefined && secure);
}, [secure]);
@@ -48,6 +48,7 @@ export function DurationPicker({
const newMinutes = Math.floor((controlledValue % 3600) / 60);
const newSeconds = controlledValue % 60;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setHours(newHours);
setMinutes(newMinutes);
setSeconds(newSeconds);
@@ -117,6 +117,7 @@ export function MiddleTruncate({ text, className }: MiddleTruncateProps) {
}, [text]);
useLayoutEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
calculateTruncation();
// Recalculate on resize (guard for jsdom/older browsers)
@@ -70,6 +70,7 @@ export function SearchInput({
// Only mark synced once we actually apply it, so a URL change during focus still syncs on blur.
if (!isFocused) {
lastSyncedRef.current = urlSearch;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setText(urlSearch);
}
}, [isControlled, controlledValue, value, isFocused, paramName]);
@@ -44,6 +44,7 @@ export default function TooltipPortal({ active = true, children }: PopperPortalP
useEffect(() => {
const el = document.createElement("div");
document.body.appendChild(el);
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setPortalElement(el);
return () => el.remove();
}, []);
@@ -985,6 +985,7 @@ function QueryTitle({
// Update rename value when title changes
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setRenameValue(title ?? "");
}, [title]);
@@ -183,6 +183,7 @@ export function QueueOverrideConcurrencyButton({
useEffect(() => {
if (navigation.state === "loading" || navigation.state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}
}, [navigation.state]);
@@ -39,6 +39,7 @@ export function AIFilterInput() {
useEffect(() => {
if (fetcher.data?.success && fetcher.state === "loading") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setText("");
setIsFocused(false);
@@ -184,6 +185,7 @@ function ErrorPopover({
useEffect(() => {
if (error) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(true);
}
if (timeout.current) {
@@ -144,6 +144,7 @@ function useChildRunStatusesTooltip({
const entry = fetcher.data.runs.find((run) => run.friendlyId === friendlyId);
if (!entry) return;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setChildStatuses((previous) =>
areChildStatusesEqual(previous, entry.statuses) ? previous : entry.statuses
);
@@ -169,6 +170,7 @@ function useChildRunStatusesTooltip({
useEffect(() => {
prevHasFinishedRef.current = hasFinished;
stopPolling();
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setChildStatuses(undefined);
if (isOpenRef.current) {
loadChildStatuses();
@@ -519,6 +519,7 @@ function TimeDropdown({
// Sync state when props change
useEffect(() => {
const parsed = getInitialCustomDuration(period);
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setCustomValue(parsed.value);
setCustomUnit(parsed.unit);
@@ -289,6 +289,7 @@ export function ToolUseRow({ tool }: { tool: ToolUse }) {
// Auto-select input tab when input arrives after initial render (e.g. streaming tool calls)
useEffect(() => {
if (!hasSubAgent && hasInput) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setActiveTab((current) => current ?? "input");
}
}, [hasInput, hasSubAgent]);
@@ -88,6 +88,7 @@ export function PurchaseSchedulesModal({
"ok" in data &&
data.ok
) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOpen(false);
}
}, [fetcher.state, fetcher.data]);
@@ -46,6 +46,7 @@ export function SampleSourcePicker({
useEffect(() => {
if (!providers || providers.length === 0) return;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSelectedProvider((current) => {
if (current && providers.some((p) => p.id === current)) return current;
if (endpointSource && providers.some((p) => p.id === endpointSource)) return endpointSource;
@@ -140,6 +140,7 @@ export function useDeliveriesLiveReload({
} = useNewDeliveriesDetection({ deliveries, isLoading });
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setVisibleDeliveries(deliveries);
resetNewDeliveriesTracking();
}, [deliveries, location.search, resetNewDeliveriesTracking]);
@@ -148,6 +149,7 @@ export function useDeliveriesLiveReload({
const data = deliveriesPollFetcher.data;
if (!data?.deliveries.length) return;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setVisibleDeliveries((current) =>
patchVisibleDeliveriesWithLiveUpdates(current, data.deliveries)
);
+1
View File
@@ -24,6 +24,7 @@ export function useEventSource(
}
// reset data if dependencies change
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
setData(null);
const eventSource = new EventSource(url, init);
@@ -214,6 +214,7 @@ export function useMetricResourceQuery(query: string, opts: MetricResourceQueryO
]);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
load();
return () => abortRef.current?.abort();
}, [load]);