From 7e7f241ec6138d35da8b3cc0d3510e42975aa14a Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 29 Aug 2026 21:02:11 -0700 Subject: [PATCH] chore: enable React correctness lints (#17285) * chore: enable React correctness lints * chore: enable additional safe lint rules --- .oxlintrc.json | 12 +++++++----- .../src/hooks/use-mobile-dictation-source.test.ts | 2 +- mobile/src/hooks/use-mobile-dictation.ts | 2 +- .../src/components/mobile/paired-mobile-devices.ts | 13 +++++++++---- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index b93f1afd619..e0c03a3b045 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -40,18 +40,18 @@ "react/no-string-refs": "error", "react/no-unescaped-entities": "error", "react/require-render-return": "error", - "react/error-boundaries": "off", - "react/globals": "off", + "react/error-boundaries": "error", + "react/globals": "error", "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/set-state-in-render": "error", "react/static-components": "off", - "react/use-memo": "off", - "react/void-use-memo": "off", + "react/use-memo": "error", + "react/void-use-memo": "error", "react/jsx-curly-brace-presence": [ "error", { "props": "never", "children": "never", "propElementValues": "always" } @@ -74,6 +74,7 @@ "typescript/consistent-type-definitions": ["error", "type"], "typescript/consistent-type-imports": "error", "typescript/no-explicit-any": ["error", { "ignoreRestArgs": true }], + "typescript/no-confusing-non-null-assertion": "error", "typescript/no-import-type-side-effects": "error", "typescript/no-unnecessary-boolean-literal-compare": "error", "typescript/no-unnecessary-template-expression": "error", @@ -143,6 +144,7 @@ { "files": ["**/*.test.*", "**/*.spec.*", "**/*-benchmark.*"], "rules": { + "react/globals": "off", "quadratic-buffer-concat/no-loop-carried-concat": "off", "renderer-scrollbar-style/require-styled-vertical-scrollbar": "off" } diff --git a/mobile/src/hooks/use-mobile-dictation-source.test.ts b/mobile/src/hooks/use-mobile-dictation-source.test.ts index 0649e7ee54b..a3192ce86ea 100644 --- a/mobile/src/hooks/use-mobile-dictation-source.test.ts +++ b/mobile/src/hooks/use-mobile-dictation-source.test.ts @@ -95,7 +95,7 @@ describe('useMobileDictation source invariants', () => { /import \{[^}]*createMobileDictationKeepAwakeOwner[^}]*\} from '\.\/mobile-dictation-keep-awake'/ ) expect(source).toContain( - 'const keepAwakeOwner = useMemo(createMobileDictationKeepAwakeOwner, [])' + 'const keepAwakeOwner = useMemo(() => createMobileDictationKeepAwakeOwner(), [])' ) expect(keepAwakeSource).toContain('activateKeepAwakeAsync') expect(keepAwakeSource).toContain('deactivateKeepAwake') diff --git a/mobile/src/hooks/use-mobile-dictation.ts b/mobile/src/hooks/use-mobile-dictation.ts index 648048dc451..7dd1689c94e 100644 --- a/mobile/src/hooks/use-mobile-dictation.ts +++ b/mobile/src/hooks/use-mobile-dictation.ts @@ -26,7 +26,7 @@ export type { UseMobileDictationResult } from './mobile-dictation-session-state' export function useMobileDictation(options: UseMobileDictationOptions): UseMobileDictationResult { const { client, enabled, onTranscript, onError } = options - const keepAwakeOwner = useMemo(createMobileDictationKeepAwakeOwner, []) + const keepAwakeOwner = useMemo(() => createMobileDictationKeepAwakeOwner(), []) const [status, setStatus] = useState('idle') const [error, setError] = useState(null) const activeIdRef = useRef(null) diff --git a/src/renderer/src/components/mobile/paired-mobile-devices.ts b/src/renderer/src/components/mobile/paired-mobile-devices.ts index d32da602ebc..206fee0584b 100644 --- a/src/renderer/src/components/mobile/paired-mobile-devices.ts +++ b/src/renderer/src/components/mobile/paired-mobile-devices.ts @@ -16,6 +16,10 @@ type PairedMobileDevicesSnapshot = { error: boolean } +type RefreshPairedMobileDevicesOptions = { + force?: boolean +} + const EMPTY_SNAPSHOT: PairedMobileDevicesSnapshot = { devices: [], loaded: false, @@ -80,9 +84,7 @@ export function replacePairedMobileDevices(devices: readonly PairedMobileDevice[ export function refreshPairedMobileDevices({ force = false -}: { - force?: boolean -} = {}): Promise { +}: RefreshPairedMobileDevicesOptions = {}): Promise { if (activeRequest && !force) { return activeRequest.promise } @@ -173,7 +175,10 @@ export function usePairedMobileDevices({ refresh: typeof refreshPairedMobileDevices } { const currentSnapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot) - const refresh = useCallback(refreshPairedMobileDevices, []) + const refresh = useCallback( + (options?: RefreshPairedMobileDevicesOptions) => refreshPairedMobileDevices(options), + [] + ) useEffect(() => { if (!enabled || !refreshOnMount || currentSnapshot.loaded || currentSnapshot.loading) {