-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
chore: adopt stable React Compiler lint rules #4737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
Comment on lines
+227
to
228
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
fd -H -t f -E node_modules -E dist 'oxlint|package\.json' . \
| xargs -r rg -n 'react(-hooks)?/(refs|set-state-in-effect|no-deriving-state-in-effects|incompatible-library)|jsPlugins|plugins'
pnpm exec oxlint --rules \
| rg 'react(-hooks)?/(refs|set-state-in-effect|no-deriving-state-in-effects|incompatible-library)'
pnpm exec oxlint \
apps/webapp/app/components/code/TSQLResultsTable.tsx \
--report-unused-disable-directivesRepository: triggerdotdev/trigger.dev Length of output: 1853 🏁 Script executed: #!/bin/bash
set -u
printf '%s\n' '--- effective Oxlint configuration ---'
cat -n .oxlintrc.json | sed -n '1,170p'
printf '%s\n' '--- Oxlint version declarations ---'
rg -n '"oxlint"|oxlint@' package.json pnpm-lock.yaml apps/webapp/package.json . 2>/dev/null | head -80
printf '%s\n' '--- CLI availability ---'
pnpm exec oxlint --version 2>&1 || true
pnpm exec oxlint --help 2>&1 | sed -n '1,180p' || true
printf '%s\n' '--- directives in the reviewed file ---'
rg -n 'oxlint-disable|react/(refs|set-state-in-effect|no-deriving-state-in-effects|incompatible-library)' apps/webapp/app/components/code/TSQLResultsTable.tsxRepository: triggerdotdev/trigger.dev Length of output: 16510 🏁 Script executed: #!/bin/bash
set +e
printf '%s\n' '--- registered rule IDs ---'
pnpm exec oxlint --rules 2>&1 \
| rg -n -C 1 'react/(refs|set-state-in-effect|no-deriving-state-in-effects|incompatible-library)' \
|| true
printf '%s\n' '--- effective file configuration ---'
pnpm exec oxlint --print-config apps/webapp/app/components/code/TSQLResultsTable.tsx 2>&1 \
| rg -n -C 1 'react/(refs|set-state-in-effect|no-deriving-state-in-effects|incompatible-library)' \
|| true
printf '%s\n' '--- unused-directive check ---'
pnpm exec oxlint \
apps/webapp/app/components/code/TSQLResultsTable.tsx \
--report-unused-disable-directives 2>&1 \
| rg -n -C 2 'unused|directive|react/(refs|set-state-in-effect|no-deriving-state-in-effects|incompatible-library)' \
|| trueRepository: triggerdotdev/trigger.dev Length of output: 259 🏁 Script executed: #!/bin/bash
set +e
printf '%s\n' '--- registered rules (raw, filtered by compiler namespace) ---'
pnpm exec oxlint --rules 2>&1 | head -40
printf '%s\n' '--- effective config (raw) ---'
pnpm exec oxlint --print-config apps/webapp/app/components/code/TSQLResultsTable.tsx 2>&1 | head -80
printf '%s\n' '--- file lint (raw) ---'
pnpm exec oxlint apps/webapp/app/components/code/TSQLResultsTable.tsx \
--report-unused-disable-directives 2>&1 | head -160Repository: triggerdotdev/trigger.dev Length of output: 6059 🌐 Web query:
💡 Result: In Oxlint v1.79.0, React Compiler support has been integrated, introducing several specific rules within the react plugin that are powered by the React Compiler's analysis [1][2]. Regarding your specific inquiries: 1. react/incompatible-library: This rule is officially supported in Oxlint [3]. It warns about the usage of library APIs known to be incompatible with manual or automatic memoization (e.g., react-hook-form's watch, TanStack Table's useReactTable) [3]. It is categorized under the "correctness" category [2]. 2. react/refs: This rule is officially supported [4]. It validates the correct usage of refs, specifically ensuring that ref.current is not read or written during the render phase [4]. It is categorized under the "correctness" category [2]. 3. react/set-state-in-effect: This rule is officially supported [5]. It disallows calling setState synchronously inside an effect body, as this triggers unnecessary extra render passes and often indicates an anti-pattern (such as deriving state in an effect) [5][6]. It is categorized under the "correctness" category [2]. 4. no-deriving-state-in-effects: There is no native Oxlint rule with the exact name "no-deriving-state-in-effects" in the core react plugin [2]. However, the behavior of deriving state in effects is targeted by the Citations:
Remove Source: MCP tools |
||
| }, [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, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
Comment on lines
+126
to
127
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
pnpm exec oxlint \
apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx \
--report-unused-disable-directivesRepository: triggerdotdev/trigger.dev Length of output: 2089 🏁 Script executed: #!/bin/bash
set -euo pipefail
file="apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx"
printf '%s\n' '--- relevant source ---'
cat -n "$file" | sed -n '95,145p;195,225p;345,420p'
printf '%s\n' '--- ref declarations and suppressions ---'
rg -n -C 2 'renderedPathRef|orderRef|navigatedRef|watchProposedRef|teardownRef|messagesRef|react/refs|eslint-disable' "$file"
printf '%s\n' '--- repository lint configuration references ---'
rg -n -S 'react/refs|oxlint|eslint-plugin-react-hooks|react-hooks' \
--glob '!node_modules/**' \
--glob '!dist/**' \
--glob '!build/**' \
.oxlintrc* package.json pnpm-lock.yaml 2>/dev/null || true
printf '%s\n' '--- package files ---'
git ls-files '*oxlint*' '*eslint*' 'package.json' | sed -n '1,120p'Repository: triggerdotdev/trigger.dev Length of output: 15588 🌐 Web query:
💡 Result: The official eslint-plugin-react-hooks includes a specific rule, react-hooks/refs, designed to enforce the correct usage of React refs [1][2][3]. This rule prohibits reading or writing to ref.current during the component render phase, as doing so can lead to unpredictable behavior, stale data, or inconsistent application state [1][4][5]. Key points regarding this rule and React ref usage include: 1. Rule Intent: The rule prevents reading or writing ref.current during rendering [1][4][3]. Because refs are meant for storing mutable values that do not trigger re-renders, accessing them during render—where React expects pure operations—breaks established React patterns [1][5][6]. 2. Acceptable Usage: - Effects and Event Handlers: You should read or write ref.current inside useEffect, useLayoutEffect, or event handlers (like onClick), where side effects are intended and allowed [1][4][3]. - Lazy Initialization: A specific, allowed exception is lazy initialization of a ref, such as if (!ref.current) ref.current = new ExpensiveValue, provided it is performed safely during the render phase [1][5][3]. 3. How the Linter Identifies Refs: The plugin uses a name-based heuristic to identify refs [1][4]. If a variable is named ref or ends in Ref, the linter tracks it as a ref object [1][3]. If you are using an object to hold non-ref mutable data and are receiving false positives from this rule, you can resolve it by renaming the variable (e.g., to box or data) so the linter no longer infers it as a React ref [1][4][3]. 4. Distinction from exhaustive-deps: While react-hooks/refs targets direct access to ref.current during render, the separate react-hooks/exhaustive-deps rule warns against including ref.current in dependency arrays [7][8]. This is because refs are mutable and their updates do not trigger re-renders; therefore, they should not be treated as dependencies that require re-running effects [7][8]. For more details, consult the official React documentation on the refs lint rule and the useRef hook [1][5]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- Oxlint configuration ---'
cat -n .oxlintrc.json | sed -n '1,180p'
printf '%s\n' '--- Change that removed the directives ---'
git diff --unified=8 -- apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx
printf '%s\n' '--- Available Oxlint rule/config capabilities ---'
pnpm exec oxlint --version
pnpm exec oxlint --help | sed -n '1,180p'Repository: triggerdotdev/trigger.dev Length of output: 15744 🏁 Script executed: #!/bin/bash
set -euo pipefail
file="apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx"
printf '%s\n' '--- parent version around the affected code ---'
git show HEAD^:"$file" 2>/dev/null | nl -ba | sed -n '115,140p;200,220p;350,420p' || true
printf '%s\n' '--- ref-rule suppression conventions ---'
rg -n -C 1 'react/refs|eslint-disable.*refs|oxlint-disable.*refs' \
--glob '!node_modules/**' \
--glob '!dist/**' \
--glob '!build/**' \
. 2>/dev/null || true
printf '%s\n' '--- locked Oxlint version ---'
sed -n '120,150p;12355,12380p;27365,27385p' pnpm-lock.yamlRepository: triggerdotdev/trigger.dev Length of output: 20178 🌐 Web query:
💡 Result: In Oxlint 1.79.0, the Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
file="apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx"
printf '%s\n' '--- parent source at affected ranges ---'
git show HEAD^:"$file" 2>/dev/null | cat -n | sed -n '115,140p;200,220p;350,420p' || true
printf '%s\n' '--- parent-to-current diff ---'
git diff --unified=3 HEAD^ HEAD -- "$file" 2>/dev/null | sed -n '1,240p' || true
printf '%s\n' '--- Oxlint refs rule documentation ---'
curl -fsSL https://oxc.rs/docs/guide/usage/linter/rules/react/refs.html | \
sed -n '1,220p' | grep -E -i -C 3 'lazy|render|current|disable' || trueRepository: triggerdotdev/trigger.dev Length of output: 50381 Restore targeted
Source: MCP tools |
||
|
|
||
| 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); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.