Skip to content

remove overloads codemod #5046

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

Merged
merged 12 commits into from
Mar 7, 2023
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
35 changes: 35 additions & 0 deletions docs/react/guides/migrating-to-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,41 @@ now we only support the object format.
+ queryClient.getQueryState(queryKey)
```

#### Codemod

To make the remove overloads migration easier, v5 comes with a codemod.

> The codemod is a best efforts attempt to help you migrate the breaking change. Please review the generated code thoroughly! Also, there are edge cases that cannot be found by the code mod, so please keep an eye on the log output.

If you want to run it against `.js` or `.jsx` files, please use the command below:

```
npx jscodeshift ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/codemods/v5/remove-overloads/remove-overloads.js
```

If you want to run it against `.ts` or `.tsx` files, please use the command below:

```
npx jscodeshift ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/codemods/v5/remove-overloads/remove-overloads.js
```

Please note in the case of `TypeScript` you need to use `tsx` as the parser; otherwise, the codemod won't be applied properly!

**Note:** Applying the codemod might break your code formatting, so please don't forget to run `prettier` and/or `eslint` after you've applied the codemod!

A few notes about how codemod works:

- Generally, we're looking for the lucky case, when the first parameter is an object expression and contains the "queryKey" or "mutationKey" property (depending on which hook/method call is being transformed). If this is the case, your code already matches the new signature, so the codemod won't touch it. 🎉
- If the condition above is not fulfilled, then the codemod will check whether the first parameter is an array expression or an identifier that references an array expression. If this is the case, the codemod will put it into an object expression, then it will be the first parameter.
- If object parameters can be inferred, the codemod will attempt to copy the already existing properties to the newly created one.
- If the codemod cannot infer the usage, then it will leave a message on the console. The message contains the file name and the line number of the usage. In this case, you need to do the migration manually.
- If the transformation results in an error, you will also see a message on the console. This message will notify you something unexpected happened, please do the migration manually.

### The `remove` method has been removed from useQuery

Previously, remove method used to remove the query from the queryCache without informing observers about it. It was best used to remove data imperatively that is no longer needed, e.g. when logging a user out.
Expand Down
39 changes: 39 additions & 0 deletions packages/codemods/src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,39 @@ module.exports = ({ root, jscodeshift }) => {
const isNewExpression = (node) =>
jscodeshift.match(node, { type: jscodeshift.NewExpression.name })

const isArrayExpression = (node) =>
jscodeshift.match(node, { type: jscodeshift.ArrayExpression.name })

const isObjectExpression = (node) =>
jscodeshift.match(node, { type: jscodeshift.ObjectExpression.name })

const isObjectProperty = (node) =>
jscodeshift.match(node, { type: jscodeshift.ObjectProperty.name })

const isSpreadElement = (node) =>
jscodeshift.match(node, { type: jscodeshift.SpreadElement.name })

/**
* @param {import('jscodeshift').Node} node
* @returns {boolean}
*/
const isFunctionDefinition = (node) => {
const isArrowFunctionExpression = jscodeshift.match(node, {
type: jscodeshift.ArrowFunctionExpression.name,
})
const isFunctionExpression = jscodeshift.match(node, {
type: jscodeshift.FunctionExpression.name,
})

return isArrowFunctionExpression || isFunctionExpression
}

const warn = (message) => {
if (process.env.NODE_ENV !== 'test') {
console.warn(message)
}
}

const isClassInstantiationOf = (node, selector) => {
if (!isNewExpression(node)) {
return false
Expand Down Expand Up @@ -158,7 +191,13 @@ module.exports = ({ root, jscodeshift }) => {
isFunctionCallOf,
isIdentifier,
isMemberExpression,
isArrayExpression,
isObjectExpression,
isObjectProperty,
isSpreadElement,
isFunctionDefinition,
locateImports,
warn,
queryClient: {
findQueryClientIdentifiers,
},
Expand Down
1 change: 0 additions & 1 deletion packages/codemods/src/v5/README.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import * as React from 'react'
import {
useIsFetching,
useIsMutating,
useQueryClient,
} from '@tanstack/react-query'
import { queryKeysFromAnotherModule } from '../another/module'

export const WithKnownParameters = () => {
useIsFetching(['foo', 'bar'])
useIsFetching(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
useIsFetching(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true }, { context: undefined })
useIsFetching(['foo', 'bar'], { type: 'all', exact: true })
useIsFetching(['foo', 'bar'], { type: 'all', exact: true }, { context: undefined })
useIsFetching({ queryKey: ['foo', 'bar'], type: 'all', exact: true })
useIsFetching({ queryKey: ['foo', 'bar'], type: 'all', exact: true }, { context: undefined })

useIsMutating(['foo', 'bar'])
useIsMutating(['foo', 'bar'], { exact: true })
useIsMutating(['foo', 'bar'], { exact: true }, { context: undefined })
useIsMutating({ mutationKey: ['foo', 'bar'], exact: true })
useIsMutating({ mutationKey: ['foo', 'bar'], exact: true }, { context: undefined })

// QueryClient methods
// --- Instantiated hook call.
const queryClient = useQueryClient()
queryClient.cancelQueries(['foo', 'bar'])
queryClient.cancelQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryClient.cancelQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true }, { silent: true })
queryClient.cancelQueries(['foo', 'bar'], { type: 'all', exact: true })
queryClient.cancelQueries(['foo', 'bar'], { type: 'all', exact: true }, { silent: true })
queryClient.cancelQueries({ queryKey: ['foo', 'bar'], type: 'all', exact: true })
queryClient.cancelQueries({ queryKey: ['foo', 'bar'], type: 'all', exact: true }, { silent: true })

queryClient.getQueriesData(['foo', 'bar'])
queryClient.getQueriesData({ queryKey: ['foo', 'bar'], type: 'all', exact: true })

queryClient.invalidateQueries(['foo', 'bar'])
queryClient.invalidateQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryClient.invalidateQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })
queryClient.invalidateQueries(['foo', 'bar'], { type: 'all', exact: true })
queryClient.invalidateQueries(['foo', 'bar'], { type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })
queryClient.invalidateQueries({ queryKey: ['foo', 'bar'], type: 'all', exact: true })
queryClient.invalidateQueries({ queryKey: ['foo', 'bar'], type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })

queryClient.isFetching(['foo', 'bar'])
queryClient.isFetching(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryClient.isFetching(['foo', 'bar'], { type: 'all', exact: true })
queryClient.isFetching({ queryKey: ['foo', 'bar'], type: 'all', exact: true })

queryClient.refetchQueries(['foo', 'bar'])
queryClient.refetchQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryClient.refetchQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })
queryClient.refetchQueries(['foo', 'bar'], { type: 'all', exact: true })
queryClient.refetchQueries(['foo', 'bar'], { type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })
queryClient.refetchQueries({ queryKey: ['foo', 'bar'], type: 'all', exact: true })
queryClient.refetchQueries({ queryKey: ['foo', 'bar'], type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })

queryClient.removeQueries(['foo', 'bar'])
queryClient.removeQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryClient.removeQueries({ queryKey: ['foo', 'bar'], type: 'all', exact: true })

queryClient.resetQueries(['foo', 'bar'])
queryClient.resetQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryClient.resetQueries(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })
queryClient.resetQueries(['foo', 'bar'], { type: 'all', exact: true })
queryClient.resetQueries(['foo', 'bar'], { type: 'all', exact: true }, { cancelRefetch: false, throwOnError: true })
queryClient.resetQueries({ queryKey: ['foo', 'bar'], exact: true })
queryClient.resetQueries({ queryKey: ['foo', 'bar'], exact: true }, { cancelRefetch: false, throwOnError: true })

queryClient.setQueriesData(['foo', 'bar'], null)
queryClient.setQueriesData(['foo', 'bar'], null, { updatedAt: 1000 })
queryClient.setQueriesData({ queryKey: ['foo', 'bar'] }, null)
queryClient.setQueriesData({ queryKey: ['foo', 'bar'] }, null, { updatedAt: 1000 })

queryClient.fetchQuery(['foo', 'bar'])
queryClient.fetchQuery(['foo', 'bar'], { queryKey: ['todos'], staleTime: 1000 })
queryClient.fetchQuery(['foo', 'bar'], { queryKey: ['todos'], queryFn: () => 'data', staleTime: 1000 })
queryClient.fetchQuery(['foo', 'bar'], () => 'data', { queryKey: ['todos'], staleTime: 1000 })
queryClient.fetchQuery(['foo', 'bar'], function myFn() { return 'data' }, { queryKey: ['todos'], staleTime: 1000 })
queryClient.fetchQuery({ queryKey: ['foo', 'bar'], queryFn: () => 'data', retry: true })

const queryCache = queryClient.getQueryCache()

queryCache.find(['foo', 'bar'])
queryCache.find(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryCache.find(['foo', 'bar'], { type: 'all', exact: true })

queryCache.findAll(['foo', 'bar'])
queryCache.findAll(['foo', 'bar'], { type: 'all', exact: true })
queryCache.findAll(['foo', 'bar'], { queryKey: ['todos'], type: 'all', exact: true })
queryCache.findAll({ queryKey: ['foo', 'bar'], type: 'all', exact: true })

return <div>Example Component</div>
}

const globalQueryKey = ['module', 'level']

export const WithIdentifiers = () => {
const queryKey = ['foo', 'bar']
const mutationKey = ['posts', 'articles']
const filters = { type: 'all', exact: true } as const
const options = { context: undefined } as const
const mutationOptions = { exact: true, fetching: false } as const
const cancelOptions = { silent: true } as const
const invalidateOptions = { cancelRefetch: true, throwOnError: true } as const
const refetchOptions = { cancelRefetch: false, throwOnError: true } as const
const resetOptions = { cancelRefetch: false, throwOnError: true } as const
const fetchOptions = { queryFn: () => 'data', retry: true } as const
const queryFn = () => 'data'

useIsFetching(queryKey)
useIsFetching(queryKey, filters)
useIsFetching(queryKey, filters, options)
useIsFetching(queryKey, { type: 'all', exact: true })
useIsFetching(queryKey, { type: 'all', exact: true }, { context: undefined })
useIsFetching(queryKey, { queryKey: ['todos'], ...filters }, options)
useIsFetching({ queryKey: queryKey, ...filters })
useIsFetching({ queryKey: queryKey, ...filters }, { context: undefined })

useIsMutating(mutationKey)
useIsMutating(mutationKey, { exact: true, status: 'idle' })
useIsMutating(mutationKey, { ...mutationOptions, exact: false })
useIsMutating({ mutationKey, ...mutationOptions })
useIsMutating({ mutationKey: ['foo', 'bar'], exact: true, status: 'idle' })

// QueryClient methods
// --- Instantiated hook call.
const queryClient = useQueryClient()
queryClient.cancelQueries(queryKey)
queryClient.cancelQueries(queryKey, filters)
queryClient.cancelQueries(queryKey, filters, cancelOptions)
queryClient.cancelQueries(queryKey, { type: 'all', exact: true })
queryClient.cancelQueries(queryKey, { type: 'all', exact: true }, { revert: true })
queryClient.cancelQueries(queryKey, { queryKey: ['todos'], ...filters }, cancelOptions)
queryClient.cancelQueries({ queryKey: queryKey, type: 'all', exact: true })
queryClient.cancelQueries({ queryKey: ['foo', 'bar'], ...filters }, cancelOptions)

queryClient.getQueriesData(globalQueryKey)
queryClient.getQueriesData({ queryKey: globalQueryKey, ...filters })
queryClient.getQueriesData({ queryKey: ['foo', 'bar'], type: 'all' })

queryClient.invalidateQueries(queryKey)
queryClient.invalidateQueries(queryKey, filters)
queryClient.invalidateQueries(queryKey, filters, invalidateOptions)
queryClient.invalidateQueries(queryKey, { queryKey: ['todos'], stale: true, ...filters })
queryClient.invalidateQueries(queryKey, { queryKey: ['todos'], stale: true, ...filters }, invalidateOptions)
queryClient.invalidateQueries({ queryKey: globalQueryKey, ...filters, stale: true })
queryClient.invalidateQueries({ queryKey: globalQueryKey, ...filters, stale: true }, invalidateOptions)

queryClient.isFetching(globalQueryKey)
queryClient.isFetching(globalQueryKey, filters)
queryClient.isFetching(globalQueryKey, { queryKey: ['todos'], type: 'all', exact: true })
queryClient.isFetching(globalQueryKey, { queryKey: ['todos'], ...filters })
queryClient.isFetching({ queryKey: globalQueryKey, ...filters, stale: true })
// Stays as it is because the code couldn't infer the type of the "queryKeysFromAnotherModule" identifier.
queryClient.isFetching(queryKeysFromAnotherModule)

queryClient.refetchQueries(queryKey)
queryClient.refetchQueries(queryKey, filters)
queryClient.refetchQueries(queryKey, filters, refetchOptions)
queryClient.refetchQueries(queryKey, { queryKey: ['todos'], ...filters }, { ...refetchOptions, cancelRefetch: true })
queryClient.refetchQueries({ queryKey: queryKey, ...filters })
queryClient.refetchQueries({ queryKey: queryKey, ...filters }, { ...refetchOptions, cancelRefetch: true })
// Stays as it is because the code couldn't infer the type of the "queryKeysFromAnotherModule" identifier.
queryClient.refetchQueries(queryKeysFromAnotherModule)
queryClient.refetchQueries(queryKeysFromAnotherModule, filters)
queryClient.refetchQueries(queryKeysFromAnotherModule, filters, refetchOptions)

queryClient.removeQueries(queryKey)
queryClient.removeQueries(queryKey, filters)
queryClient.removeQueries(queryKey, { queryKey: ['todos'], ...filters, stale: true })
queryClient.removeQueries({ queryKey, ...filters, stale: true })
// Stays as it is because the code couldn't infer the type of the "queryKeysFromAnotherModule" identifier.
queryClient.removeQueries(queryKeysFromAnotherModule)
queryClient.removeQueries(queryKeysFromAnotherModule, filters)

queryClient.resetQueries(queryKey)
queryClient.resetQueries(queryKey, filters)
queryClient.resetQueries(queryKey, filters, resetOptions)
queryClient.resetQueries(queryKey, { queryKey: ['todos'], ...filters, stale: true })
queryClient.resetQueries(queryKey, { queryKey: ['todos'], ...filters, stale: true }, resetOptions)
queryClient.resetQueries({ queryKey, ...filters, stale: true })
queryClient.resetQueries({ queryKey, ...filters, stale: true }, resetOptions)
// Stays as it is because the code couldn't infer the type of the "queryKeysFromAnotherModule" identifier.
queryClient.resetQueries(queryKeysFromAnotherModule)
queryClient.resetQueries(queryKeysFromAnotherModule, filters)
queryClient.resetQueries(queryKeysFromAnotherModule, filters, resetOptions)

queryClient.fetchQuery(queryKey)
queryClient.fetchQuery(queryKey, fetchOptions)
queryClient.fetchQuery(queryKey, { networkMode: 'always', ...fetchOptions })
queryClient.fetchQuery(queryKey, queryFn, fetchOptions)
queryClient.fetchQuery(queryKey, () => 'data', { networkMode: 'always', ...fetchOptions })
// Stays as it is because the code couldn't infer the type of the "queryKeysFromAnotherModule" identifier.
queryClient.fetchQuery(queryKeysFromAnotherModule)
queryClient.fetchQuery(queryKeysFromAnotherModule, fetchOptions)
queryClient.fetchQuery(queryKeysFromAnotherModule, queryFn, fetchOptions)
}
Loading