From 7f8eeec0492a269601a402e238010ff623567cb9 Mon Sep 17 00:00:00 2001 From: vinamra1102 Date: Thu, 21 May 2026 11:31:32 +0530 Subject: [PATCH 1/2] fix(eslint-plugin-query): handle array-destructured useQueries results in no-unstable-deps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `collectVariableNames` function in the `no-unstable-deps` rule only handled `Identifier` patterns. When users array-destructure `useQueries` or `useSuspenseQueries` results (e.g. `const [q1, q2] = useQueries(...)`), the individual variables were not tracked as unstable. This meant passing them directly to React hook dependency arrays was never flagged. Extend `collectVariableNames` to also handle `ArrayPattern` — including plain identifier elements and rest elements. Fixes #10746 --- .../fix-no-unstable-deps-array-pattern.md | 5 + .../src/__tests__/no-unstable-deps.test.ts | 92 +++++++++++++++++++ .../no-unstable-deps/no-unstable-deps.rule.ts | 14 +++ 3 files changed, 111 insertions(+) create mode 100644 .changeset/fix-no-unstable-deps-array-pattern.md diff --git a/.changeset/fix-no-unstable-deps-array-pattern.md b/.changeset/fix-no-unstable-deps-array-pattern.md new file mode 100644 index 00000000000..a0da3857512 --- /dev/null +++ b/.changeset/fix-no-unstable-deps-array-pattern.md @@ -0,0 +1,5 @@ +--- +"@tanstack/eslint-plugin-query": patch +--- + +fix(no-unstable-deps): handle array-destructured useQueries and useSuspenseQueries results diff --git a/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts b/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts index b111fc0ed73..1edb2adac27 100644 --- a/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts +++ b/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts @@ -64,6 +64,25 @@ const baseTestCases = { `, }, ]) + .concat([ + { + name: `should pass when useQueries is array-destructured and element properties are used with ${reactHookAlias}`, + code: ` + ${reactHookImport} + import { useQueries } from "@tanstack/react-query"; + + function Component() { + const [{ data }] = useQueries({ + queries: [ + { queryKey: ['test'], queryFn: () => 'test' } + ] + }); + const callback = ${reactHookInvocation}(() => { data }, [data]); + return; + } + `, + }, + ]) .concat([ { name: `should pass when useQuery is imported from non-TanStack source and used with ${reactHookAlias}`, @@ -163,6 +182,79 @@ const baseTestCases = { }, ], }, + ]) + .concat([ + { + name: `array-destructured useQueries element is passed to ${reactHookInvocation} as dependency`, + code: ` + ${reactHookImport} + import { useQueries } from "@tanstack/react-query"; + + function Component() { + const [userQuery, postsQuery] = useQueries({ + queries: [ + { queryKey: ['user'], queryFn: () => 'user' }, + { queryKey: ['posts'], queryFn: () => 'posts' } + ] + }); + const callback = ${reactHookInvocation}(() => { userQuery.data }, [userQuery]); + return; + } + `, + errors: [ + { + messageId: 'noUnstableDeps', + data: { reactHook: reactHookAlias, queryHook: 'useQueries' }, + }, + ], + }, + { + name: `array-destructured useSuspenseQueries element is passed to ${reactHookInvocation} as dependency`, + code: ` + ${reactHookImport} + import { useSuspenseQueries } from "@tanstack/react-query"; + + function Component() { + const [query] = useSuspenseQueries({ + queries: [ + { queryKey: ['test'], queryFn: () => 'test' } + ] + }); + const callback = ${reactHookInvocation}(() => { query.data }, [query]); + return; + } + `, + errors: [ + { + messageId: 'noUnstableDeps', + data: { reactHook: reactHookAlias, queryHook: 'useSuspenseQueries' }, + }, + ], + }, + { + name: `rest element of array-destructured useQueries is passed to ${reactHookInvocation} as dependency`, + code: ` + ${reactHookImport} + import { useQueries } from "@tanstack/react-query"; + + function Component() { + const [firstQuery, ...restQueries] = useQueries({ + queries: [ + { queryKey: ['a'], queryFn: () => 'a' }, + { queryKey: ['b'], queryFn: () => 'b' } + ] + }); + const callback = ${reactHookInvocation}(() => {}, [restQueries]); + return; + } + `, + errors: [ + { + messageId: 'noUnstableDeps', + data: { reactHook: reactHookAlias, queryHook: 'useQueries' }, + }, + ], + }, ]), } diff --git a/packages/eslint-plugin-query/src/rules/no-unstable-deps/no-unstable-deps.rule.ts b/packages/eslint-plugin-query/src/rules/no-unstable-deps/no-unstable-deps.rule.ts index 32e68464682..a5fc2c2b94a 100644 --- a/packages/eslint-plugin-query/src/rules/no-unstable-deps/no-unstable-deps.rule.ts +++ b/packages/eslint-plugin-query/src/rules/no-unstable-deps/no-unstable-deps.rule.ts @@ -64,6 +64,20 @@ export const rule = createRule({ ) { if (pattern.type === AST_NODE_TYPES.Identifier) { trackedVariables[pattern.name] = queryHook + } else if (pattern.type === AST_NODE_TYPES.ArrayPattern) { + for (const element of pattern.elements) { + if (element === null) { + continue + } + if (element.type === AST_NODE_TYPES.Identifier) { + trackedVariables[element.name] = queryHook + } else if ( + element.type === AST_NODE_TYPES.RestElement && + element.argument.type === AST_NODE_TYPES.Identifier + ) { + trackedVariables[element.argument.name] = queryHook + } + } } } From 5372162b528a352442242a459f4e3de5332fdd80 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 18:39:10 +0000 Subject: [PATCH 2/2] ci: apply automated fixes --- .../src/__tests__/no-unstable-deps.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts b/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts index 43fbdc01426..43bbe0f494f 100644 --- a/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts +++ b/packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts @@ -245,7 +245,10 @@ const baseTestCases = { errors: [ { messageId: 'noUnstableDeps', - data: { reactHook: reactHookAlias, queryHook: 'useSuspenseQueries' }, + data: { + reactHook: reactHookAlias, + queryHook: 'useSuspenseQueries', + }, }, ], },