Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions examples/react/algolia/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { tanstackConfig } from '@tanstack/config/eslint'
import pluginQuery from '@tanstack/eslint-plugin-query'
import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'

export default [
...tanstackConfig,
...pluginQuery.configs['flat/recommended'],
pluginReact.configs.recommended,
reactHooks.configs.recommended,
{
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
},
},
]
8 changes: 0 additions & 8 deletions examples/react/basic-graphql-request/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { tanstackConfig } from '@tanstack/config/eslint'
import pluginQuery from '@tanstack/eslint-plugin-query'
import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'

export default [
...tanstackConfig,
...pluginQuery.configs['flat/recommended'],
pluginReact.configs.recommended,
reactHooks.configs.recommended,
{
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
},
},
]
8 changes: 0 additions & 8 deletions examples/react/basic/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { tanstackConfig } from '@tanstack/config/eslint'
import pluginQuery from '@tanstack/eslint-plugin-query'
import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'

export default [
...tanstackConfig,
...pluginQuery.configs['flat/recommended'],
pluginReact.configs.recommended,
reactHooks.configs.recommended,
{
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
},
},
]
8 changes: 0 additions & 8 deletions examples/react/shadow-dom/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { tanstackConfig } from '@tanstack/config/eslint'
import pluginQuery from '@tanstack/eslint-plugin-query'
import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'

export default [
...tanstackConfig,
...pluginQuery.configs['flat/recommended'],
pluginReact.configs.recommended,
reactHooks.configs.recommended,
{
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
},
},
]
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@vitest/eslint-plugin": "^1.1.36",
"esbuild-plugin-file-path-extensions": "^2.1.4",
"eslint": "^9.36.0",
"eslint-plugin-react-hooks": "^6.0.0-rc.2",
"eslint-plugin-react-hooks": "^6.1.1",
"jsdom": "^27.0.0",
"knip": "^5.63.1",
"markdown-link-extractor": "^4.0.2",
Expand Down
12 changes: 5 additions & 7 deletions packages/react-query-devtools/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
// @ts-check

import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'
import reactHooks from 'eslint-plugin-react-hooks'
import rootConfig from './root.eslint.config.js'

export default [
...rootConfig,
reactHooks.configs.recommended,
// @ts-expect-error wtf
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive-by comment as I saw your posts on Bluesky...

if you hadn't already come across it, thought it'd be helpful to point to facebook/react#34745, which explains (some of?) the type issues with integrating the plugin's config (and the expect error overrides you needed).

Issue was closed but the fix hasn't been published yet. You may have a better time with integration once it is 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ts-expect-error should go away if the types become correct

...reactHooks.configs['recommended-latest'],
{
files: ['**/*.{ts,tsx}'],
...pluginReact.configs.recommended,
rules: {
'@eslint-react/no-context-provider': 'off', // We need to be React 18 compatible
},
},
{
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/react-compiler': 'error',
'react-hooks/unsupported-syntax': 'error',
'react-hooks/incompatible-library': 'error',
},
},
]
13 changes: 5 additions & 8 deletions packages/react-query-next-experimental/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
// @ts-check

import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'
import reactHooks from 'eslint-plugin-react-hooks'
import rootConfig from './root.eslint.config.js'

export default [
...rootConfig,
reactHooks.configs.recommended,
// @ts-expect-error wtf
...reactHooks.configs['recommended-latest'],
{
files: ['**/*.{ts,tsx}'],
...pluginReact.configs.recommended,
rules: {
'@eslint-react/no-context-provider': 'off', // We need to be React 18 compatible
},
},
{
rules: {
'@eslint-react/no-unstable-context-value': 'off',
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/react-compiler': 'error',
'react-hooks/unsupported-syntax': 'error',
'react-hooks/incompatible-library': 'error',
},
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export function createHydrationStreamProvider<TShape>() {
.join(',')

// Flush stream
// eslint-disable-next-line react-hooks/react-compiler
// eslint-disable-next-line react-hooks/immutability
stream.length = 0

const html: Array<string> = [
Expand Down Expand Up @@ -169,6 +169,7 @@ export function createHydrationStreamProvider<TShape>() {

onEntries(...winStream)

// eslint-disable-next-line react-hooks/immutability
win[id] = {
initialized: true,
push: onEntries,
Expand Down
12 changes: 5 additions & 7 deletions packages/react-query-persist-client/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
// @ts-check

import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'
import reactHooks from 'eslint-plugin-react-hooks'
import rootConfig from './root.eslint.config.js'

export default [
...rootConfig,
reactHooks.configs.recommended,
// @ts-expect-error wtf
...reactHooks.configs['recommended-latest'],
Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace unprofessional comment with a clear explanation.

The comment "wtf" is unprofessional and doesn't explain why the type error is being suppressed. Replace it with a clear explanation of the type issue.

-  // @ts-expect-error wtf
+  // @ts-expect-error - recommended-latest config has incomplete type definitions
   ...reactHooks.configs['recommended-latest'],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// @ts-expect-error wtf
...reactHooks.configs['recommended-latest'],
// @ts-expect-error - recommended-latest config has incomplete type definitions
...reactHooks.configs['recommended-latest'],
🤖 Prompt for AI Agents
In packages/react-query-persist-client/eslint.config.js around lines 9 to 10,
replace the unprofessional "// @ts-expect-error wtf" comment with a concise,
professional explanation of the suppression: state which type incompatibility or
declaration mismatch is happening (e.g., that
reactHooks.configs['recommended-latest'] has an incompatible or missing type
declaration for this project's ESLint config), why the suppression is necessary
(to accept the runtime shape despite TypeScript complaining), and include a TODO
linking to an issue or task to resolve the typing mismatch later; keep it short
and informative.

{
files: ['**/*.{ts,tsx}'],
...pluginReact.configs.recommended,
rules: {
'@eslint-react/no-context-provider': 'off', // We need to be React 18 compatible
},
},
{
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/react-compiler': 'error',
'react-hooks/unsupported-syntax': 'error',
'react-hooks/incompatible-library': 'error',
},
},
]
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,6 @@ describe('PersistQueryClientProvider', () => {
const state = useQuery({
queryKey: key,
queryFn: async () => {
// eslint-disable-next-line react-hooks/react-compiler
fetched = true
await sleep(10)
return 'fetched'
Expand Down
15 changes: 6 additions & 9 deletions packages/react-query/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
// @ts-check

import pluginReact from '@eslint-react/eslint-plugin'
import * as reactHooks from 'eslint-plugin-react-hooks'
import reactHooks from 'eslint-plugin-react-hooks'
import rootConfig from './root.eslint.config.js'

export default [
...rootConfig,
reactHooks.configs.recommended,
// @ts-expect-error wtf
...reactHooks.configs['recommended-latest'],
{
files: ['**/*.{ts,tsx}'],
...pluginReact.configs.recommended,
rules: {
'@eslint-react/no-context-provider': 'off', // We need to be React 18 compatible
},
},
{
rules: {
'@eslint-react/dom/no-missing-button-type': 'off',
'react-hooks/react-compiler': 'error',
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/unsupported-syntax': 'error',
'react-hooks/incompatible-library': 'error',
},
},
{
files: ['**/__tests__/**'],
rules: {
'@eslint-react/dom/no-missing-button-type': 'off',
'@typescript-eslint/no-unnecessary-condition': 'off',
'react-hooks/react-compiler': 'off',
},
},
]
5 changes: 4 additions & 1 deletion packages/react-query/src/HydrationBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export const HydrationBoundary = ({
const client = useQueryClient(queryClient)

const optionsRef = React.useRef(options)
optionsRef.current = options
React.useEffect(() => {
optionsRef.current = options
})

// This useMemo is for performance reasons only, everything inside it must
// be safe to run in every render and code here should be read as "in render".
Expand Down Expand Up @@ -89,6 +91,7 @@ export const HydrationBoundary = ({
if (newQueries.length > 0) {
// It's actually fine to call this with queries/state that already exists
// in the cache, or is older. hydrate() is idempotent for queries.
// eslint-disable-next-line react-hooks/refs
hydrate(client, { queries: newQueries }, optionsRef.current)
}
if (existingQueries.length > 0) {
Expand Down
5 changes: 3 additions & 2 deletions packages/react-query/src/__tests__/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4684,6 +4684,7 @@ describe('useQuery', () => {

function Page(props: { limit: number }) {
const state = useQuery({ queryKey: [key, props.limit], queryFn })
// eslint-disable-next-line react-hooks/immutability
states[props.limit] = state
return (
<div>
Expand Down Expand Up @@ -6152,7 +6153,6 @@ describe('useQuery', () => {
const key = queryKey()

function Page() {
const mounted = React.useRef<boolean>(false)
const { data, status } = useQuery({
enabled: false,
queryKey: key,
Expand All @@ -6162,9 +6162,10 @@ describe('useQuery', () => {
},
})

const mounted = React.useRef<boolean>(null)
// this simulates a synchronous update between the time the query is created
// and the time it is subscribed to that could be missed otherwise
if (!mounted.current) {
if (mounted.current === null) {
Comment on lines +6165 to +6168
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix type annotation for null initialization.

The ref is typed as React.useRef<boolean> but initialized with null. This is a type mismatch that violates TypeScript's type safety.

Apply this diff to correct the type:

-      const mounted = React.useRef<boolean>(null)
+      const mounted = React.useRef<boolean | null>(null)

The usage pattern (initialize with null, then set to true) requires the union type boolean | null to properly represent all possible values the ref can hold.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mounted = React.useRef<boolean>(null)
// this simulates a synchronous update between the time the query is created
// and the time it is subscribed to that could be missed otherwise
if (!mounted.current) {
if (mounted.current === null) {
const mounted = React.useRef<boolean | null>(null)
// this simulates a synchronous update between the time the query is created
// and the time it is subscribed to that could be missed otherwise
if (mounted.current === null) {
🤖 Prompt for AI Agents
In packages/react-query/src/__tests__/useQuery.test.tsx around lines 6165 to
6168, the ref is declared as React.useRef<boolean> but initialized with null
causing a TypeScript type mismatch; update the ref's generic type to include
null (boolean | null) so it can be initialized with null and later set to true,
e.g., change the declaration to useRef<boolean | null>(null).

mounted.current = true
queryClient.setQueryData(key, 1)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('useSuspenseInfiniteQuery', () => {
initialPageParam: 1,
getNextPageParam: () => 1,
// @ts-expect-error
// eslint-disable-next-line react-hooks/purity
queryFn: Math.random() >= 0 ? skipToken : () => Promise.resolve(5),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ describe('UseSuspenseQueries config object overload', () => {
)
})

it('should not show type error when using spreaded queryOptions', () => {
it('should not show type error when using rest queryOptions', () => {
assertType(
useSuspenseQueries({
queries: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ describe('useSuspenseQueries 2', () => {
}

function Page() {
// eslint-disable-next-line react-hooks/purity
const ref = React.useRef(Math.random())
const result = useSuspenseQueries({
queries: [
Expand Down Expand Up @@ -665,6 +666,7 @@ describe('useSuspenseQueries 2', () => {
{
queryKey: key,
// @ts-expect-error
// eslint-disable-next-line react-hooks/purity
queryFn: Math.random() >= 0 ? skipToken : () => Promise.resolve(5),
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ describe('useSuspenseQuery', () => {
useSuspenseQuery({
queryKey: key,
// @ts-expect-error
// eslint-disable-next-line react-hooks/purity
queryFn: Math.random() >= 0 ? skipToken : () => Promise.resolve(5),
})

Expand Down
2 changes: 1 addition & 1 deletion packages/react-query/src/useMutationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function useMutationState<TResult = MutationState>(
const mutationCache = useQueryClient(queryClient).getMutationCache()
const optionsRef = React.useRef(options)
const result = React.useRef<Array<TResult>>(null)
if (!result.current) {
if (result.current === null) {
result.current = getResult(mutationCache, options)
}

Expand Down
Loading
Loading