chore: adopt stable React Compiler lint rules (#4737)

This commit is contained in:
Chris Arderne
2026-08-20 13:17:40 +01:00
committed by GitHub
parent ce40d0259f
commit 4392e79ce2
108 changed files with 332 additions and 304 deletions
+35 -1
View File
@@ -70,6 +70,19 @@
"react/require-render-return": "error",
"react/style-prop-object": "error",
"react/void-dom-elements-no-children": "error",
"react/error-boundaries": "off",
"react/globals": "off",
"react/immutability": "off",
"react/incompatible-library": "off",
"react/preserve-manual-memoization": "off",
"react/purity": "off",
"react/refs": "off",
"react/set-state-in-effect": "off",
"react/set-state-in-render": "off",
"react/static-components": "off",
"react/unsupported-syntax": "off",
"react/use-memo": "off",
"react/void-use-memo": "off",
"react/checked-requires-onchange-or-readonly": "error",
"react/forward-ref-uses-ref": "error",
"react/iframe-missing-sandbox": "error",
@@ -124,7 +137,22 @@
"react/button-has-type": "error",
"react/jsx-no-useless-fragment": "error",
"react/no-unstable-nested-components": "error",
"react/react-compiler": "error",
"react/error-boundaries": "error",
"react/globals": "error",
"react/hooks": "error",
"react/immutability": "error",
"react/incompatible-library": "error",
"react/memo-dependencies": "error",
"react/no-deriving-state-in-effects": "error",
"react/preserve-manual-memoization": "error",
"react/purity": "error",
"react/refs": "error",
"react/set-state-in-effect": "error",
"react/set-state-in-render": "error",
"react/static-components": "error",
"react/unsupported-syntax": "error",
"react/use-memo": "error",
"react/void-use-memo": "error",
"react/rules-of-hooks": "error",
"trigger-runops/no-control-plane-run-graph-access": "error",
"trigger-runops/no-control-plane-in-runops-slot": "error"
@@ -136,6 +164,12 @@
"react/rules-of-hooks": "error"
}
},
{
"files": ["**/*.ts", "**/*.tsx"],
"rules": {
"no-redeclare": "off"
}
},
{
"files": ["apps/webapp/app/**/*.test.ts", "apps/webapp/app/**/*.test.tsx"],
"rules": {
+1 -1
View File
@@ -273,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setFeedbackGivenForQAs(new Set());
}
}, [conversation.length]);
+2 -2
View File
@@ -55,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsConnected(undefined);
return;
}
@@ -114,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setCrossEngineIsConnected(isConnected);
return;
}
+1 -1
View File
@@ -76,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- 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,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setRandomQuote(quotes[randomIndex]);
}, []);
@@ -25,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsSplineReady(true);
return;
}
@@ -56,7 +56,7 @@ export function FeatureFlagsDialog({
const saveFetcher = useFetcher<ActionData>();
const loadFeatureFlags = loadFetcher.load;
const onOpenChangeRef = useRef(onOpenChange);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
onOpenChangeRef.current = onOpenChange;
const [overrides, setOverrides] = useState<Record<string, unknown>>({});
@@ -68,7 +68,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSaveError(null);
setOverrides({});
setInitialOverrides({});
@@ -79,7 +79,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOverrides({ ...loaded });
setInitialOverrides({ ...loaded });
}
@@ -89,7 +89,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSaveError(saveFetcher.data.error);
}
}, [saveFetcher.data]);
@@ -34,12 +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.
// oxlint-disable-next-line react/set-state-in-effect -- 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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
if (savedJustNow && !hasFieldErrors) setIsEditing(false);
}, [savedJustNow, hasFieldErrors]);
@@ -65,12 +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.
// oxlint-disable-next-line react/set-state-in-effect -- 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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
if (savedJustNow && !hasFieldErrors) setIsEditing(false);
}, [savedJustNow, hasFieldErrors]);
@@ -119,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setShowResetBanner(true);
if (searchParams.get("alertsReset") !== "1") {
@@ -141,12 +141,12 @@ 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. */
/* oxlint-disable react/preserve-manual-memoization -- 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 */
/* oxlint-enable react/preserve-manual-memoization */
const savedEmails = useMemo(() => alerts.emails, [alerts.emails]);
const hasLegacySpikes = hasLegacySpikeAlertLevels(
alerts,
@@ -190,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setThresholdRows(toThresholdRows(savedThresholds));
setEmailValues(savedEmails.length > 0 ? [...savedEmails, ""] : [""]);
}, [savedThresholds, savedEmails]);
@@ -126,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMode(resetMode);
setCustomAmount(savedCustomAmount);
setCancelInProgressRuns(savedCancelInProgressRuns);
@@ -63,7 +63,7 @@ export function BillingLimitRecoveryPanel({
const formRef = useRef<HTMLFormElement>(null);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- A refreshed server recommendation intentionally resets this editable amount draft.
// oxlint-disable-next-line react/set-state-in-effect, react/no-deriving-state-in-effects -- A refreshed server recommendation intentionally resets this editable amount draft.
setNewAmount(String(suggestedNewLimitDollars));
}, [suggestedNewLimitDollars]);
@@ -61,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMode("new");
}
}, [mode, canEdit]);
@@ -94,7 +94,7 @@ export function JSONEditor(opts: JSONEditorProps) {
const editor = useRef<HTMLDivElement>(null);
const settings: Omit<UseCodeMirror, "onBlur"> = {
...opts,
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
container: editor.current,
extensions,
editable: !readOnly,
@@ -196,7 +196,7 @@ export function TSQLEditor(opts: TSQLEditorProps) {
const settings: Omit<UseCodeMirror, "onBlur"> = {
...opts,
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
container: editor.current,
extensions,
editable: !readOnly,
@@ -224,7 +224,7 @@ const DebouncedInput = forwardRef<
const [value, setValue] = useState(initialValue);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- Programmatic filter changes intentionally reset the debounced input draft.
// oxlint-disable-next-line react/set-state-in-effect, react/no-deriving-state-in-effects -- Programmatic filter changes intentionally reset the debounced input draft.
setValue(initialValue);
}, [initialValue]);
@@ -1065,7 +1065,6 @@ function ColumnResizeHandle({ header }: { header: Header<RowData, unknown> }) {
}
/* oxlint-enable jsx-a11y/no-static-element-interactions */
// oxlint-disable-next-line react/react-compiler -- TanStack Table is not compatible with compiler memoization.
export const TSQLResultsTable = memo(function TSQLResultsTable({
rows,
columns,
@@ -1122,6 +1121,7 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
// Column resize mode: 'onChange' for real-time feedback, 'onEnd' for performance
const columnResizeMode: ColumnResizeMode = "onChange";
// oxlint-disable-next-line react/incompatible-library -- TanStack Table is not compatible with compiler memoization.
const table = useReactTable({
data: rows,
columns: columnDefs,
@@ -48,7 +48,7 @@ export function TextEditor(opts: TextEditorProps) {
const editor = useRef<HTMLDivElement>(null);
const settings: Omit<UseCodeMirror, "onBlur"> = {
...opts,
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
container: editor.current,
extensions,
editable: !readOnly,
@@ -93,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState({ status: "error", error: "No environment context to run the query." });
return;
}
@@ -123,7 +123,7 @@ export function DashboardAgentChat({
// The path this chat last rendered on. React never unmounts on a page teardown, so an
// unmount whose live URL has moved is the router having navigated out from under it.
const renderedPathRef = useRef(location.pathname);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
renderedPathRef.current = location.pathname;
const transport = useTriggerChatTransport<typeof dashboardAgent>({
@@ -210,7 +210,7 @@ export function DashboardAgentChat({
});
const orderRef = useRef(createTranscriptOrder(initialMessages));
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
const messages = orderTranscript(rawMessages, orderRef.current);
// Read here, not in the panel, so it re-reads as each turn settles.
@@ -361,7 +361,7 @@ export function DashboardAgentChat({
const navigatedRef = useRef<Set<string> | null>(null);
if (navigatedRef.current === null) {
navigatedRef.current = new Set();
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
pendingNavigateIntents(initialMessages, navigatedRef.current);
}
useEffect(() => {
@@ -377,7 +377,7 @@ export function DashboardAgentChat({
const watchProposedRef = useRef<Set<string> | null>(null);
if (watchProposedRef.current === null) {
watchProposedRef.current = new Set();
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
pendingWatchIntents(initialMessages, watchProposedRef.current);
}
useEffect(() => {
@@ -392,7 +392,7 @@ export function DashboardAgentChat({
}, [transport, chatId, aiStop]);
const teardownRef = useRef<() => void>(() => {});
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
teardownRef.current = () => {
if (status !== "streaming" && status !== "submitted") return;
const reason = unmountTeardown({
@@ -406,7 +406,7 @@ export function DashboardAgentChat({
// Read by the settle effect, which must not re-run when the transcript changes.
const messagesRef = useRef(messages);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
messagesRef.current = messages;
const prevStatus = useRef(status);
@@ -126,9 +126,9 @@ export function winningInvestigationOccurrences(messages: UIMessage[]): Map<stri
function useInvestigationWinners(messages: UIMessage[]): Map<string, string> {
const previous = useRef<Map<string, string>>();
const next = useMemo(() => winningInvestigationOccurrences(messages), [messages]);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
previous.current = reuseWinners(previous.current, next);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
return previous.current;
}
@@ -168,13 +168,11 @@ 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]);
const loadHistory = useMemo(
() =>
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
createCoalescedReload(async () => {
try {
const res = await fetch(actionPath);
@@ -315,7 +313,6 @@ 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);
@@ -344,7 +341,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
@@ -361,7 +358,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 () => {
@@ -389,7 +386,6 @@ 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,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState(computeInitialState());
}
prevIsOpenRef.current = isOpen;
@@ -263,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- 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"));
}
@@ -331,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsRedirecting(false);
}
}, [isOpen]);
@@ -393,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("project-selection");
}
}, [state, onboardingData?.availableProjects, onboardingData?.authInvalid]);
@@ -404,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("env-var-sync");
}
}, [state, onboardingData?.environmentVariables, onboardingData?.authInvalid]);
@@ -420,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("loading-env-mapping");
if (onDataReload) {
onDataReload();
@@ -443,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("env-mapping");
} else {
setState("loading-env-vars");
@@ -668,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("completed");
}
}, [completeOnboardingFetcher.data, completeOnboardingFetcher.state, state]);
@@ -683,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("completed");
}
}, [state, isGitHubConnectedForOnboarding, fromMarketplaceContext, nextUrl, trackOnboarding]);
@@ -713,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setState("loading-env-vars");
}
}, [envMappingFetcher.data, envMappingFetcher.state]);
@@ -729,14 +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.
// oxlint-disable-next-line react/set-state-in-effect -- 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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSelectedVercelProject(availableProjects[0]);
}
}, [state, availableProjects, selectedVercelProject]);
@@ -68,7 +68,6 @@ 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/${
@@ -81,7 +80,6 @@ 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,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setShowLoadMoreSpinner(false);
return;
}
@@ -88,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setSelectedDashboardId(available?.friendlyId ?? customDashboards[0].friendlyId);
}
}, [customDashboards, selectedDashboardId, widgetLimit]);
@@ -52,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}
}, [navigation.formAction, navigation.state, formAction]);
@@ -61,7 +61,6 @@ 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]);
@@ -250,7 +249,7 @@ function Branches({
}, []);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes local state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -49,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setMenuOpen(false);
}, [navigation.location?.pathname, navigation.location?.search]);
@@ -445,7 +445,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setCustomizeConfirmPending(false);
setCustomizeError("Couldn't save your changes. Please try again.");
return;
@@ -532,7 +532,7 @@ export function SideMenu({
// object each render, so depending on it would fire the cleanup (flushing the debounce) every
// render — and drags re-render constantly — instead of only on unmount.
const flushPendingPreferencesRef = useRef<() => void>();
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
flushPendingPreferencesRef.current = () => {
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current);
@@ -598,7 +598,7 @@ export function SideMenu({
}, []);
// Animate width + progress over COLLAPSE_ANIM_MS (toggle button, ⌘B shortcut, release-snap).
/* oxlint-disable react/react-compiler -- The animation step is local to each callback invocation. */
const animateTo = useCallback(
(targetWidth: number, targetProgress: number) => {
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
@@ -629,7 +629,6 @@ export function SideMenu({
},
[writeVisual]
);
/* oxlint-enable react/react-compiler */
// Collapse/expand to a resting state and remember it.
const applyCollapsed = useCallback(
@@ -648,7 +647,7 @@ export function SideMenu({
// Drag runs on window-level listeners so releasing anywhere finalizes it. (Pointer capture alone
// was unreliable: if the browser drops it mid-drag, the release never fires and the menu strands.)
/* oxlint-disable react/react-compiler -- Drag handlers share invocation-local state and listeners. */
const onHandlePointerDown = useCallback(
(e: ReactPointerEvent<HTMLDivElement>) => {
if (e.button !== 0) return;
@@ -758,7 +757,6 @@ export function SideMenu({
},
[animateTo, applyCollapsed, persistSideMenuPreferences, writeVisual]
);
/* oxlint-enable react/react-compiler */
// Keep the drag handlers' collapsed mirror in sync; tear down any in-flight animation/drag on unmount.
useEffect(() => {
@@ -1497,7 +1495,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOpen(false);
}, [navigation.location?.pathname, navigation.location?.search]);
@@ -1711,7 +1709,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOrgMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -1990,7 +1988,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}, [navigation.location?.pathname]);
@@ -2045,7 +2043,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -2167,7 +2165,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}, [navigation.location?.pathname]);
@@ -19,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setHeaderMenuOpen(false);
}, [navigation.location?.pathname]);
@@ -34,7 +34,7 @@ export function useReorderableList<T>({
const [order, setOrder] = useState<string[]>(() => initialOrder ?? items.map(itemKey));
const resetOrderRef = useRef({ initialOrder, items, itemKey });
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
resetOrderRef.current = { initialOrder, items, itemKey };
// Only an organization switch resets user-managed order. Keep the latest inputs in a ref so
@@ -290,9 +290,9 @@ export function AgentDotMatrix({
const playlistKey = playlist.join(",");
const paletteObjRef = useRef(paletteObj);
const playlistRef = useRef(playlist);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
paletteObjRef.current = paletteObj;
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
playlistRef.current = playlist;
useEffect(() => {
@@ -41,14 +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.
// oxlint-disable-next-line react/set-state-in-effect -- 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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setRendered(true);
return;
}
@@ -261,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setShowSpinner(false);
return;
}
@@ -111,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsChecked(defaultChecked ?? false);
}, [defaultChecked]);
@@ -120,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsSecure(secure !== undefined && secure);
}, [secure]);
@@ -81,7 +81,7 @@ export function DateField({
});
const stateValueRef = useRef(state.value);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
stateValueRef.current = state.value;
// Sync only when the passed value or timezone mode changes. Depending on state.value directly
@@ -372,7 +372,7 @@ export const RelativeDateTime = ({ date, timeZone, capitalize = true }: Relative
// On first render
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- A changed date intentionally resets the timer-backed relative text.
// oxlint-disable-next-line react/set-state-in-effect, react/no-deriving-state-in-effects -- A changed date intentionally resets the timer-backed relative text.
setRelativeText(getRelativeText(realDate, capitalize));
}, [realDate, capitalize]);
@@ -48,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setHours(newHours);
setMinutes(newMinutes);
setSeconds(newSeconds);
@@ -10,7 +10,6 @@ type InputNumberStepperProps = Omit<JSX.IntrinsicElements["input"], "min" | "max
controlSize?: "base" | "large";
};
// oxlint-disable-next-line react/react-compiler -- The compiler cannot currently rewrite these hoisted function references.
export function InputNumberStepper({
value,
onChange,
@@ -117,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
calculateTruncation();
// Recalculate on resize (guard for jsdom/older browsers)
@@ -100,9 +100,9 @@ function collapsibleHandleClassName(show: boolean) {
function useFrozenValue<T>(value: T | null | undefined): T | null | undefined {
const ref = useRef(value);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
if (value != null) ref.current = value;
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
return ref.current;
}
@@ -70,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setText(urlSearch);
}
}, [isControlled, controlledValue, value, isFocused, paramName]);
@@ -39,7 +39,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setPortalElement(el);
return () => el.remove();
}, []);
@@ -193,7 +193,6 @@ export type UseTreeStateOutput = {
scrollToNode: (id: string) => void;
};
// oxlint-disable-next-line react/react-compiler -- TanStack Virtual is not compatible with compiler memoization.
export function useTree<TData, TFilterValue>({
tree,
selectedId,
@@ -277,6 +276,7 @@ export function useTree<TData, TFilterValue>({
dispatch({ type: "UPDATE_FILTER", payload: { filter: latestFilterRef.current } });
}, [serializedFilterValue]);
// oxlint-disable-next-line react/incompatible-library -- TanStack Virtual is not compatible with compiler memoization.
const virtualizer = useVirtualizer({
count: state.visibleNodeIds.length,
getItemKey: (index) => state.visibleNodeIds[index],
@@ -55,7 +55,7 @@ export function useZoomSelection(): UseZoomSelectionReturn {
const stateRef = useRef<ZoomSelectionState>(state);
// Keep ref in sync with state
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
stateRef.current = state;
const startSelection = useCallback((label: string) => {
@@ -505,7 +505,7 @@ export function QueryEditor({
// Use a ref so the effect can read chartConfig without re-firing on every config tweak
const chartConfigRef = useRef(chartConfig);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
chartConfigRef.current = chartConfig;
// Reset chart config only when a column referenced by the current config is no
@@ -563,7 +563,7 @@ export function QueryEditor({
}, []);
// Compute current save data for the save render prop
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
const currentQuery = editorRef.current?.getQuery() ?? "";
const saveData: QueryEditorSaveData = {
title: queryTitle ?? "Untitled Query",
@@ -792,7 +792,7 @@ export function QueryEditor({
onRename={handleRenameTitle}
/>
}
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
query={editorRef.current?.getQuery() ?? defaultQuery}
data={{
rows: results.rows,
@@ -847,7 +847,7 @@ export function QueryEditor({
<ResultsChart
rows={results.rows}
columns={results.columns}
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
query={editorRef.current?.getQuery() ?? defaultQuery}
chartConfig={chartConfig}
onChartConfigChange={handleChartConfigChange}
@@ -897,7 +897,7 @@ export function QueryEditor({
<ResultsBigNumber
rows={results.rows}
columns={results.columns}
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
query={editorRef.current?.getQuery() ?? defaultQuery}
bigNumberConfig={bigNumberConfig}
onBigNumberConfigChange={setBigNumberConfig}
@@ -960,7 +960,7 @@ export function QueryEditor({
{mode.type === "standalone" && (
<SaveToDashboardDialog
title={queryTitle ?? "Untitled Query"}
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
query={editorRef.current?.getQuery() ?? ""}
config={
resultsView === "table"
@@ -991,7 +991,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setRenameValue(title ?? "");
}, [title]);
@@ -183,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(false);
}
}, [navigation.state]);
@@ -39,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setText("");
setIsFocused(false);
@@ -185,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setIsOpen(true);
}
if (timeout.current) {
@@ -100,7 +100,7 @@ function useChildRunStatusesTooltip({
key: `child-statuses-${friendlyId}`,
});
const fetcherStateRef = useRef(fetcher.state);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
fetcherStateRef.current = fetcher.state;
const [childStatuses, setChildStatuses] = useState<ChildStatusEntry[] | undefined>();
@@ -121,7 +121,7 @@ function useChildRunStatusesTooltip({
// Keep the latest loader callback available to the polling interval
// without recreating the interval on every render.
const loadChildStatusesRef = useRef(loadChildStatuses);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
loadChildStatusesRef.current = loadChildStatuses;
const stopPolling = useCallback(() => {
@@ -146,7 +146,6 @@ 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
);
@@ -172,7 +171,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,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setCustomValue(parsed.value);
setCustomUnit(parsed.unit);
@@ -285,7 +285,6 @@ function useAgentSessionMessages({
// `scheduleFlush`. The Map *reference* changes on every flush so React
// detects the state update and the downstream `useMemo` recomputes.
const [messagesById, setMessagesById] = useState<Map<string, UIMessage>>(
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
() => new Map(pendingRef.current)
);
@@ -295,7 +294,7 @@ function useAgentSessionMessages({
const lastFlushAtRef = useRef<number>(0);
const pendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const scheduleFlush = useRef<() => void>(() => {});
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
scheduleFlush.current = () => {
if (pendingTimerRef.current !== null) return; // already scheduled
const now = Date.now();
@@ -672,7 +671,7 @@ function useAgentSessionMessages({
return useMemo(() => {
const timestamps = timestampsRef.current;
const arr = Array.from(messagesById.values());
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
arr.sort((a, b) => {
const ta = timestamps.get(a.id) ?? 0;
const tb = timestamps.get(b.id) ?? 0;
@@ -289,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setActiveTab((current) => current ?? "input");
}
}, [hasInput, hasSubAgent]);
@@ -2,10 +2,10 @@ import { useVirtualizer } from "@tanstack/react-virtual";
import { useRef } from "react";
import { SelectItem } from "../primitives/Select";
// oxlint-disable-next-line react/react-compiler -- TanStack Virtual is not compatible with compiler memoization.
export function TimezoneList({ timezones }: { timezones: string[] }) {
const parentRef = useRef<HTMLDivElement>(null);
// oxlint-disable-next-line react/incompatible-library -- TanStack Virtual is not compatible with compiler memoization.
const rowVirtualizer = useVirtualizer({
count: timezones.length,
getScrollElement: () => parentRef.current,
@@ -75,7 +75,7 @@ export function PurchaseSchedulesModal({
useEffect(() => {
if (!open) return;
// oxlint-disable-next-line react/react-compiler -- Keep the open draft aligned with authoritative billing values.
// oxlint-disable-next-line react/set-state-in-effect -- Keep the open draft aligned with authoritative billing values.
setBundles(Math.round(extraSchedules / stepSize));
}, [open, extraSchedules, stepSize]);
@@ -88,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setOpen(false);
}
}, [fetcher.state, fetcher.data]);
@@ -46,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- 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;
@@ -124,7 +124,7 @@ export function useDeliveriesLiveReload({
const location = useLocation();
const deliveriesPollFetcher = useTypedFetcher<typeof liveDeliveriesLoader>();
const deliveriesPollFetcherStateRef = useRef(deliveriesPollFetcher.state);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
deliveriesPollFetcherStateRef.current = deliveriesPollFetcher.state;
const [visibleDeliveries, setVisibleDeliveries] = useState(deliveries);
@@ -141,7 +141,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setVisibleDeliveries(deliveries);
resetNewDeliveriesTracking();
}, [deliveries, location.search, resetNewDeliveriesTracking]);
@@ -150,7 +150,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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setVisibleDeliveries((current) =>
patchVisibleDeliveriesWithLiveUpdates(current, data.deliveries)
);
+1 -1
View File
@@ -11,7 +11,7 @@ export function useAutoRevalidate(options: UseAutoRevalidateOptions = {}) {
const { interval = 5000, onFocus = true, disabled = false } = options;
const revalidator = useRevalidator();
const revalidatorRef = useRef(revalidator);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
revalidatorRef.current = revalidator;
useEffect(() => {
+2 -2
View File
@@ -12,9 +12,9 @@ export function useChanged<T extends { id: string }>(
const itemRef = useRef<T | undefined>();
const itemId = item?.id;
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
actionRef.current = action;
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
itemRef.current = item;
useEffect(() => {
+3 -3
View File
@@ -207,7 +207,7 @@ export function useDashboardEditor({
const isInitializedRef = useRef(false);
const currentLayoutJsonRef = useRef<string>(JSON.stringify(initialData.layout));
const initialDataRef = useRef(initialData);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
initialDataRef.current = initialData;
// Sync queue to prevent race conditions
@@ -258,7 +258,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. */
/* oxlint-disable react/preserve-manual-memoization -- 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) {
@@ -311,7 +311,7 @@ export function useDashboardEditor({
processNextSync();
}
}, [widgetActionUrl, layoutActionUrl, onSyncError]);
/* oxlint-enable react/react-compiler */
/* oxlint-enable react/preserve-manual-memoization */
// -------------------------------------------------------------------------
// Queue helpers
+1 -1
View File
@@ -29,7 +29,7 @@ export function useDebounceEffect<T>(value: T, fn: (value: T) => void, delay: nu
const fnRef = useRef(fn);
// Update the ref whenever the function changes
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
fnRef.current = fn;
useEffect(() => {
@@ -8,7 +8,7 @@ export function useElementVisibility({ onVisibilityChange }: UseElementVisibilit
const ref = useRef<HTMLDivElement>(null);
const isVisibleRef = useRef(false);
const callbackRef = useRef(onVisibilityChange);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative integration outside React state.
callbackRef.current = onVisibilityChange;
useEffect(() => {
+1 -1
View File
@@ -24,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
setData(null);
const eventSource = new EventSource(url, init);
@@ -214,7 +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.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes local state after an external or lifecycle change.
load();
return () => abortRef.current?.abort();
}, [load]);
@@ -243,7 +243,7 @@ export default function Page() {
const usefulLinksPanelRef = useRef<PanelHandle>(null);
const fetcher = useFetcher();
const fetcherRef = useRef(fetcher);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
fetcherRef.current = fetcher;
const toggleUsefulLinks = useCallback((show: boolean) => {
@@ -617,7 +617,7 @@ function NewApiKeyDialog({
}
if (actionData?.ok && actionData.action === "create") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setCreatedApiKey(actionData.apiKey);
} else if (actionData && !actionData.ok) {
setShowError(true);
@@ -652,7 +652,7 @@ function PurchaseBranchesModal({
const [amountValue, setAmountValue] = useState(extraBranches);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- The authoritative branch count intentionally resets this modal draft.
// oxlint-disable-next-line react/set-state-in-effect, react/no-deriving-state-in-effects -- The authoritative branch count intentionally resets this modal draft.
setAmountValue(extraBranches);
}, [extraBranches]);
const isLoading = fetcher.state !== "idle";
@@ -667,7 +667,7 @@ function PurchaseBranchesModal({
"ok" in data &&
data.ok
) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setOpen(false);
}
}, [fetcher.state, fetcher.data]);
@@ -648,7 +648,7 @@ function PurchaseConcurrencyModal({
const [open, setOpen] = useState(false);
useEffect(() => {
if (purchaseSucceeded) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setOpen(false);
setSearchParams((s) => {
s.delete("success");
@@ -444,7 +444,7 @@ function useContainerWidth(initialWidth = 1280) {
useEffect(() => {
measureWidth();
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setMounted(true);
const element = containerRef.current;
@@ -677,14 +677,14 @@ function RenameDashboardDialog({ title }: { title: string }) {
// Close dialog when navigation completes
useEffect(() => {
if (navigation.state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsOpen(false);
}
}, [navigation.state]);
// Sync newTitle state when title changes (after successful rename)
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- A completed rename intentionally resets this editable title draft.
// oxlint-disable-next-line react/set-state-in-effect, react/no-deriving-state-in-effects -- A completed rename intentionally resets this editable title draft.
setNewTitle(title);
}, [title]);
@@ -751,7 +751,7 @@ function DeleteDashboardDialog({ title }: { title: string }) {
// Close dialog when navigation completes
useEffect(() => {
if (navigation.state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsOpen(false);
}
}, [navigation.state]);
@@ -214,7 +214,7 @@ export default function Page() {
const abortController = new AbortController();
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setLogs([]);
setStreamError(null);
setIsStreaming(true);
@@ -632,7 +632,7 @@ function LogsDisplay({
const logsContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- Deployment status changes intentionally reset the user-controlled collapse state.
// oxlint-disable-next-line react/set-state-in-effect, react/no-deriving-state-in-effects -- Deployment status changes intentionally reset the user-controlled collapse state.
setCollapsed(initialCollapsed);
}, [initialCollapsed]);
@@ -451,7 +451,7 @@ function EnvironmentVariablesListPage({
const [isVirtualized, setIsVirtualized] = useState(false);
useLayoutEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsVirtualized(shouldVirtualize);
}, [shouldVirtualize]);
@@ -744,7 +744,6 @@ function EnvironmentVariableTableRow({
);
}
// oxlint-disable-next-line react/react-compiler -- TanStack Virtual is not compatible with compiler memoization.
function EnvironmentVariablesVirtualTableBody({
groupedEnvironmentVariables,
scrollRef,
@@ -758,6 +757,7 @@ function EnvironmentVariablesVirtualTableBody({
vercelIntegration: PageVercelIntegration | null;
columnCount: number;
}) {
// oxlint-disable-next-line react/incompatible-library -- TanStack Virtual is not compatible with compiler memoization.
const rowVirtualizer = useVirtualizer({
count: groupedEnvironmentVariables.length,
getScrollElement: () => scrollRef.current,
@@ -816,7 +816,7 @@ function EditEnvironmentVariablePanel({
// Close dialog on successful submission
useEffect(() => {
if (lastSubmission?.success && fetcher.state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsOpen(false);
}
}, [lastSubmission?.success, fetcher.state]);
@@ -318,7 +318,7 @@ function LogsList({
// Clear accumulated logs immediately when filters change (for instant visual feedback)
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setAccumulatedLogs([]);
setNextCursor(undefined);
// Preserve log selection from URL param, clear if not present
@@ -328,7 +328,7 @@ function LogsList({
// Populate accumulated logs when new data arrives
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setAccumulatedLogs(list.logs);
setNextCursor(list.pagination.next);
}, [list.logs, list.pagination.next]);
@@ -207,7 +207,7 @@ function PlaygroundChat() {
activeConversation?.clientData ? JSON.stringify(activeConversation.clientData, null, 2) : "{}"
);
const clientDataJsonRef = useRef(clientDataJson);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
clientDataJsonRef.current = clientDataJson;
const [machine, setMachine] = useState<string | undefined>(undefined);
const [tags, setTags] = useState<string[]>([]);
@@ -268,14 +268,14 @@ function PlaygroundChat() {
// silently ignored on the first send. Mirror the `clientDataJsonRef`
// pattern so the transport always calls the latest `startSession`.
const startSessionRef = useRef(startSession);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
startSessionRef.current = startSession;
// Create TriggerChatTransport directly (not via useTriggerChatTransport hook
// to avoid React version mismatch between SDK and webapp)
const transportRef = useRef<TriggerChatTransport | null>(null);
if (transportRef.current === null) {
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
transportRef.current = new TriggerChatTransport({
task: agent.slug,
// The Remix action is idempotent on `(env, externalId)` and
@@ -304,7 +304,7 @@ function PlaygroundChat() {
: {}),
});
}
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
const transport = transportRef.current;
// Keep the transport's `defaultMetadata` in sync with the JSON editor.
@@ -355,7 +355,7 @@ function PlaygroundChat() {
);
// useChat from AI SDK — handles message accumulation, streaming, stop
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
const { messages, sendMessage, stop, status, error } = useChat({
id: chatId,
messages: initialMessages,
@@ -396,10 +396,10 @@ function PlaygroundChat() {
inputRef.current?.focus();
}, [isEmpty]);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
const session = transport.getSession(chatId);
/* oxlint-disable react/react-compiler -- The transport and chat ID are stable for this component's lifetime. */
/* oxlint-disable react/memo-dependencies -- The transport and chat ID are stable for this component's lifetime. */
const handlePreload = useCallback(async () => {
setPreloading(true);
try {
@@ -410,7 +410,7 @@ function PlaygroundChat() {
setPreloading(false);
}
}, [transport, chatId]);
/* oxlint-enable react/react-compiler */
/* oxlint-enable react/memo-dependencies */
const handleNewConversation = useCallback(() => {
// Navigate without ?conversation= so the loader returns activeConversation=null
@@ -1177,7 +1177,7 @@ function usePlaygroundPendingMessages({
[status, transport, chatId, sendMessage, metadata]
);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
const pending: PendingMessageEntry[] = pendingMsgs.map((m) => ({
id: m.id,
text: m.parts[0]?.text ?? "",
@@ -909,7 +909,7 @@ function OverrideDialog({
// Reset when dialog opens
useEffect(() => {
if (open) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setEditedContent(content);
setCommitMessage("");
setModel(currentOverrideModel ?? prompt.defaultModel ?? "");
@@ -1333,7 +1333,7 @@ function GenerationsTab({
// Append fetched rows when fetcher completes
useEffect(() => {
if (fetcher.data && fetcher.state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setGenerations((prev) => {
const existingIds = new Set(prev.map((g) => g.span_id));
const newRows = fetcher.data!.generations.filter((g) => !existingIds.has(g.span_id));
@@ -1424,7 +1424,7 @@ function GenerationsTab({
const [showSpinner, setShowSpinner] = useState(false);
useEffect(() => {
if (!isLoadingMore) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setShowSpinner(false);
return;
}
@@ -1035,7 +1035,7 @@ function EnvironmentPauseResumeButton({
useEffect(() => {
if (navigation.state === "loading" || navigation.state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsOpen(false);
}
}, [navigation.state]);
@@ -899,7 +899,7 @@ function useConcurrencyKeys(opts: {
}, [body]);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
load();
return () => abortRef.current?.abort();
}, [load]);
@@ -1264,7 +1264,7 @@ function TimelineView({
const [duration, setDuration] = useState(queueAdjustedNs(totalDuration, queuedDuration));
useEffect(() => {
if (rootSpanStatus !== "executing" || !rootStartedAt) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setDuration(queueAdjustedNs(totalDuration, queuedDuration));
return;
}
@@ -165,7 +165,7 @@ export function useRunsLiveReload({
const location = useLocation();
const runsPollFetcher = useTypedFetcher<typeof liveRunsLoader>();
const runsPollFetcherStateRef = useRef(runsPollFetcher.state);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
runsPollFetcherStateRef.current = runsPollFetcher.state;
const [visibleRuns, setVisibleRuns] = useState(runs);
@@ -193,7 +193,7 @@ export function useRunsLiveReload({
// Single reset path: new loader data or changed filters re-baseline both the
// visible rows and new-run tracking.
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setVisibleRuns(runs);
resetNewRunsTracking();
}, [runs, searchKeyWithoutPagination, resetNewRunsTracking]);
@@ -204,7 +204,7 @@ export function useRunsLiveReload({
const data = runsPollFetcher.data;
if (!data?.runs.length) return;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setVisibleRuns((currentRuns) => patchVisibleRunsWithLiveUpdates(currentRuns, data.runs));
}, [runsPollFetcher.data]);
@@ -261,7 +261,6 @@ const ROW_NUMBER_COL_MIN_CH = 3;
const TIME_COL_WIDTH = "7rem";
const TYPE_COL_WIDTH = "5rem";
// oxlint-disable-next-line react/react-compiler -- TanStack Virtual is not compatible with compiler memoization.
function RawConversationView({
inResourcePath,
outResourcePath,
@@ -387,6 +386,7 @@ function RawConversationView({
return () => cancelAnimationFrame(raf);
}, [merged, isAtBottom]);
// oxlint-disable-next-line react/incompatible-library -- TanStack Virtual is not compatible with compiler memoization.
const rowVirtualizer = useVirtualizer({
count: merged.length,
getScrollElement: () => scrollRef.current,
@@ -261,7 +261,7 @@ export default function IntegrationsSettingsPage() {
if (onboardingData && vercelFetcher.state === "idle") {
// Data is loaded, ensure modal is open (query param takes precedence)
if (!isModalOpen) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
openVercelOnboarding();
}
} else if (vercelFetcher.state === "idle" && !hasVercelFetcherData) {
@@ -290,7 +290,7 @@ export default function IntegrationsSettingsPage() {
if (hasQueryParam && !isModalOpen) {
// Query param is present but modal is closed, open it
// This ensures the modal stays open during the onboarding flow
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
openVercelOnboarding();
}
}, [hasQueryParam, isModalOpen, openVercelOnboarding]);
@@ -300,7 +300,7 @@ export default function IntegrationsSettingsPage() {
if (hasQueryParam && onboardingData && vercelFetcher.state === "idle") {
// Data loaded and query param is present, ensure modal is open
if (!isModalOpen) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
openVercelOnboarding();
}
}
@@ -445,7 +445,7 @@ function BuildSettingsForm({ buildSettings }: { buildSettings: BuildSettings })
buildSettingsValues.installCommand !== (buildSettings?.installCommand || "") ||
buildSettingsValues.triggerConfigFilePath !== (buildSettings?.triggerConfigFilePath || "") ||
buildSettingsValues.useNativeBuildServer !== nativeBuildServerEnabled;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setHasBuildSettingsChanges(hasChanges);
}, [buildSettingsValues, buildSettings, nativeBuildServerEnabled]);
@@ -651,7 +651,7 @@ function ScheduleSheet({
// Always reopen in inspect mode.
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setMode("inspect");
}, [openScheduleId]);
@@ -694,7 +694,7 @@ function ScheduleSheet({
handledUpdateRef.current = data;
if (data.ok) {
toast.success(data.message ?? "Schedule updated");
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setMode("inspect");
if (detailPath) loadScheduleDetail(detailPath);
revalidator.revalidate();
@@ -1585,7 +1585,7 @@ function RunTemplatesPopover({
useEffect(() => {
if (lastSubmission && "success" in lastSubmission && lastSubmission.success === true) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsDeleteDialogOpen(false);
}
}, [lastSubmission]);
@@ -1773,7 +1773,7 @@ function CreateTemplateModal({
useEffect(() => {
if (lastSubmission && "success" in lastSubmission && lastSubmission.success === true) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsModalOpen(false);
setShowCreatedSuccessMessage(true);
clearTimeout(successMessageTimeoutRef.current);
@@ -614,7 +614,7 @@ function SetSecretDialog({
// Close on a successful save; the loader revalidates and the state flips to "Set".
useEffect(() => {
if (fetcher.state === "idle" && fetcher.data?.success) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setOpen(false);
}
}, [fetcher.state, fetcher.data]);
@@ -375,7 +375,7 @@ function useOverrideDraft<T>(serverValue: T): {
const [override, setOverride] = useState<{ value: T } | null>(null);
useEffect(() => {
// Server matches the pending edit → clear the override.
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setOverride((current) => (current && Object.is(current.value, serverValue) ? null : current));
}, [serverValue]);
const value = override ? override.value : serverValue;
@@ -420,7 +420,7 @@ export default function Page() {
useEffect(() => {
if (portalFetcher.data?.ok && portalFetcher.data.url) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setPortalUrl(portalFetcher.data.url);
}
}, [portalFetcher.data]);
@@ -919,7 +919,7 @@ function DirectorySyncSection({
// server value so polled-in groups appear and matched overrides drop.
const [draftGroupRoles, setDraftGroupRoles] = useState<Record<string, string>>({});
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setDraftGroupRoles((current) => {
const next: Record<string, string> = {};
for (const g of directorySync.groups) {
@@ -970,7 +970,7 @@ export function PurchaseSeatsModal({
const [amountValue, setAmountValue] = useState(extraSeats);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- The authoritative seat count intentionally resets this modal draft.
// oxlint-disable-next-line react/set-state-in-effect, react/no-deriving-state-in-effects -- The authoritative seat count intentionally resets this modal draft.
setAmountValue(extraSeats);
}, [extraSeats]);
const isLoading = fetcher.state !== "idle";
@@ -985,7 +985,7 @@ export function PurchaseSeatsModal({
"ok" in data &&
data.ok
) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setOpen(false);
}
}, [fetcher.state, fetcher.data]);
@@ -360,7 +360,7 @@ export default function Page() {
useEffect(() => {
const nonOther = workingOnOptions.filter((o) => o !== WORKING_ON_OTHER);
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setShuffledWorkingOn([...shuffleArray(nonOther), WORKING_ON_OTHER]);
const nonOtherGoals = goalOptions.filter((o) => o !== GOALS_OTHER);
@@ -221,7 +221,7 @@ export default function Page() {
const [contrastPreview, setContrastPreview] = useState(contrast);
useEffect(() => {
if (contrastFetcher.state === "idle") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setContrastPreview(contrast);
document.documentElement.style.setProperty("--theme-contrast", String(contrast / 100));
}
@@ -182,14 +182,14 @@ export default function AdminFeatureFlagsRoute() {
editable[key] = value;
}
}
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setValues({ ...editable });
setInitialValues({ ...editable });
}, [globalFlags, unlocked]);
useEffect(() => {
if (saveFetcher.data?.success) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setSaveError(null);
setConfirmOpen(false);
} else if (saveFetcher.data?.error) {
@@ -65,7 +65,7 @@ export default function AdminQueueMetricsRoute() {
const handledSaveDataRef = useRef(saveFetcher.data);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setEnabled(controls.enabled);
setSampleRate(String(controls.sampleRate));
}, [controls.enabled, controls.sampleRate]);
@@ -77,7 +77,7 @@ export default function AdminQueueMetricsRoute() {
handledSaveDataRef.current = saveFetcher.data;
if (saveFetcher.data.success) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setError(null);
revalidate();
} else if (saveFetcher.data.error) {
@@ -228,7 +228,7 @@ export default function Page() {
useEffect(() => {
const nonOtherReferral = referralSourceOptions.filter((r) => r !== "Other");
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setShuffledReferralSources([...shuffleArray(nonOtherReferral), "Other"]);
const nonOtherRoles = roleOptions.filter((r) => r !== "Other");
+1 -1
View File
@@ -197,7 +197,7 @@ export default function LoginMfaPage() {
// Reset hideError when a new error appears
React.useEffect(() => {
if (rawMfaError) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setHideError(false);
}
}, [rawMfaError]);
@@ -178,10 +178,12 @@ export function useMfaSetup(initialIsEnabled: boolean) {
disableMethod: "totp",
});
const fetcherData = fetcher.data;
// Handle fetcher responses
useEffect(() => {
if (fetcher.data) {
const { data } = fetcher;
if (fetcherData) {
const data = fetcherData;
switch (data.action) {
case "enable-mfa":
@@ -222,7 +224,7 @@ export function useMfaSetup(initialIsEnabled: boolean) {
break;
}
}
}, [fetcher.data]);
}, [fetcherData]);
// Handle submitting state
useEffect(() => {
@@ -99,7 +99,7 @@ export function NewBranchPanel({
s.delete("dialogClosed");
return s;
});
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsOpen(false);
}
}, [searchParams, setSearchParams]);
+4 -4
View File
@@ -207,7 +207,7 @@ export function MetricWidget({
// Track the latest props so the submit callback always uses fresh values
// without needing to be recreated (which would cause useInterval to re-register listeners).
const propsRef = useRef(props);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
propsRef.current = props;
// Track visibility so we only fetch for on-screen widgets.
@@ -220,7 +220,7 @@ export function MetricWidget({
},
});
/* oxlint-disable react/react-compiler -- These ref objects are stable callback inputs. */
/* oxlint-disable react/memo-dependencies -- These ref objects are stable callback inputs. */
const submit = useCallback(() => {
if (!isVisibleRef.current) {
isDirtyRef.current = true;
@@ -265,8 +265,8 @@ export function MetricWidget({
}
});
}, [isVisibleRef]);
/* oxlint-enable react/react-compiler */
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
/* oxlint-enable react/memo-dependencies */
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
submitRef.current = submit;
// Clean up on unmount
@@ -442,7 +442,7 @@ export function ConnectGitHubRepoModal({
const params = new URLSearchParams(searchParams);
if (params.get("openGithubRepoModal") === "1") {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsModalOpen(true);
params.delete("openGithubRepoModal");
setSearchParams(params);
@@ -451,7 +451,7 @@ export function ConnectGitHubRepoModal({
useEffect(() => {
if (lastSubmission && "success" in lastSubmission && lastSubmission.success === true) {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setIsModalOpen(false);
}
}, [lastSubmission]);
@@ -812,7 +812,7 @@ export function ConnectedGitHubRepoForm({
gitSettingsValues.stagingBranch !==
(connectedGitHubRepo.branchTracking?.staging?.branch || "") ||
gitSettingsValues.previewDeploymentsEnabled !== connectedGitHubRepo.previewDeploymentsEnabled;
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setHasGitSettingsChanges(hasChanges);
}, [gitSettingsValues, connectedGitHubRepo]);
@@ -105,7 +105,6 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
);
};
// oxlint-disable-next-line react/react-compiler -- TanStack Virtual is not compatible with compiler memoization.
export function RealtimeStreamViewer({
runId,
streamKey,
@@ -254,6 +253,7 @@ export function RealtimeStreamViewer({
.length;
// Virtual rendering for list view
// oxlint-disable-next-line react/incompatible-library -- TanStack Virtual is not compatible with compiler memoization.
const rowVirtualizer = useVirtualizer({
count: chunks.length,
getScrollElement: () => scrollRef.current,
@@ -540,7 +540,7 @@ export function useRealtimeStream(resourcePath: string, startIndex?: number) {
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setChunks([]);
setError(null);
@@ -1118,7 +1118,7 @@ function VercelSettingsPanel({
useEffect(() => {
if (!data?.authInvalid && !hasError && !data && !hasFetched) {
load(vercelResourcePath(organizationSlug, projectSlug, environmentSlug));
// oxlint-disable-next-line react/react-compiler -- This effect intentionally synchronizes route state after an external or lifecycle change.
// oxlint-disable-next-line react/set-state-in-effect -- This effect intentionally synchronizes route state after an external or lifecycle change.
setHasFetched(true);
}
}, [
@@ -249,7 +249,7 @@ function CompleteDateTimeWaitpointForm({
const project = useProject();
const environment = useEnvironment();
// oxlint-disable-next-line react/react-compiler -- This form intentionally snapshots wall-clock time for its deadline UI.
// oxlint-disable-next-line react/purity -- This form intentionally snapshots wall-clock time for its deadline UI.
const now = Date.now();
const timeToComplete = waitpoint.completedAfter.getTime() - now;
if (timeToComplete < 0) {
@@ -368,7 +368,7 @@ function CompleteManualWaitpointForm({ waitpoint }: { waitpoint: { id: string }
<div className="max-h-[70vh] min-h-40 overflow-y-auto bg-background-deep scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control">
<JSONEditor
autoFocus
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
defaultValue={currentJson.current}
readOnly={false}
basicSetup
@@ -59,7 +59,7 @@ export function AIGeneratedCronField({ onSuccess }: AIGeneratedCronFieldProps) {
const fetcher = useFetcher<typeof action>();
const [text, setText] = useState<string>("");
const onSuccessRef = useRef(onSuccess);
// oxlint-disable-next-line react/react-compiler -- This ref intentionally coordinates an imperative route integration outside React state.
// oxlint-disable-next-line react/refs -- This ref intentionally coordinates an imperative route integration outside React state.
onSuccessRef.current = onSuccess;
const organization = useOrganization();
const project = useProject();

Some files were not shown because too many files have changed in this diff Show More