From 0f26de3084ced1a370c3635d8059114218965d19 Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 1 Nov 2023 11:58:28 -0300 Subject: [PATCH 1/4] change signature of hooks --- .eslintrc.js | 2 + lib/api/useApiQuery.tsx | 11 +- lib/hooks/useGetCsrfToken.tsx | 38 +++--- lib/hooks/useIsSafeAddress.tsx | 14 +- package.json | 5 +- ui/apiKey/ApiKeyModal/ApiKeyForm.tsx | 5 +- ui/customAbi/CustomAbiModal/CustomAbiForm.tsx | 5 +- ui/marketplace/useMarketplaceApps.tsx | 17 ++- ui/pages/MarketplaceApp.tsx | 16 +-- ui/privateTags/AddressModal/AddressForm.tsx | 29 ++-- .../TransactionModal/TransactionForm.tsx | 35 ++--- .../PublicTagsForm/PublicTagsForm.tsx | 5 +- ui/shared/DeleteModal.tsx | 5 +- ui/shared/nft/useNftMediaType.tsx | 13 +- ui/snippets/footer/Footer.tsx | 13 +- ui/snippets/networkMenu/useNetworkMenu.tsx | 13 +- ui/watchlist/AddressModal/AddressForm.tsx | 3 +- ui/watchlist/WatchlistTable/WatchListItem.tsx | 19 +-- .../WatchlistTable/WatchListTableItem.tsx | 19 +-- yarn.lock | 124 +++++++++++------- 20 files changed, 213 insertions(+), 178 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 763a684cf9..f225b17b7a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -20,6 +20,7 @@ module.exports = { 'plugin:@typescript-eslint/recommended', 'plugin:jest/recommended', 'plugin:playwright/playwright-test', + 'plugin:@tanstack/eslint-plugin-query/recommended', ], plugins: [ 'es5', @@ -31,6 +32,7 @@ module.exports = { 'eslint-plugin-import-helpers', 'jest', 'eslint-plugin-no-cyrillic-string', + '@tanstack/query', ], parser: '@typescript-eslint/parser', parserOptions: { diff --git a/lib/api/useApiQuery.tsx b/lib/api/useApiQuery.tsx index 5b3634f2e7..0d28e70c54 100644 --- a/lib/api/useApiQuery.tsx +++ b/lib/api/useApiQuery.tsx @@ -23,12 +23,15 @@ export default function useApiQuery( ) { const apiFetch = useApiFetch(); - return useQuery, ResourceError, ResourcePayload>( - getResourceKey(resource, { pathParams, queryParams }), - async() => { + return useQuery, ResourceError, ResourcePayload>({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: getResourceKey(resource, { pathParams, queryParams }), + queryFn: async() => { // all errors and error typing is handled by react-query // so error response will never go to the data // that's why we are safe here to do type conversion "as Promise>" return apiFetch(resource, { pathParams, queryParams, fetchParams }) as Promise>; - }, queryOptions); + }, + ...queryOptions, + }); } diff --git a/lib/hooks/useGetCsrfToken.tsx b/lib/hooks/useGetCsrfToken.tsx index 98706689ca..297f1567c2 100644 --- a/lib/hooks/useGetCsrfToken.tsx +++ b/lib/hooks/useGetCsrfToken.tsx @@ -10,27 +10,29 @@ import useFetch from 'lib/hooks/useFetch'; export default function useGetCsrfToken() { const nodeApiFetch = useFetch(); - useQuery(getResourceKey('csrf'), async() => { - if (!isNeedProxy()) { - const url = buildUrl('csrf'); - const apiResponse = await fetch(url, { credentials: 'include' }); - const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf'); + useQuery({ + queryKey: getResourceKey('csrf'), + queryFn: async() => { + if (!isNeedProxy()) { + const url = buildUrl('csrf'); + const apiResponse = await fetch(url, { credentials: 'include' }); + const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf'); - if (!csrfFromHeader) { - Sentry.captureException(new Error('Client fetch failed'), { tags: { - source: 'fetch', - 'source.resource': 'csrf', - 'status.code': 500, - 'status.text': 'Unable to obtain csrf token from header', - } }); - return; - } + if (!csrfFromHeader) { + Sentry.captureException(new Error('Client fetch failed'), { tags: { + source: 'fetch', + 'source.resource': 'csrf', + 'status.code': 500, + 'status.text': 'Unable to obtain csrf token from header', + } }); + return; + } - return { token: csrfFromHeader }; - } + return { token: csrfFromHeader }; + } - return nodeApiFetch('/node-api/csrf'); - }, { + return nodeApiFetch('/node-api/csrf'); + }, enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)), }); } diff --git a/lib/hooks/useIsSafeAddress.tsx b/lib/hooks/useIsSafeAddress.tsx index ec727a2d19..49062f7cc2 100644 --- a/lib/hooks/useIsSafeAddress.tsx +++ b/lib/hooks/useIsSafeAddress.tsx @@ -8,20 +8,18 @@ const feature = config.features.safe; export default function useIsSafeAddress(hash: string | undefined): boolean { const fetch = useFetch(); - const { data } = useQuery( - [ 'safe_transaction_api', hash ], - async() => { + const { data } = useQuery({ + queryKey: [ 'safe_transaction_api', hash ], + queryFn: async() => { if (!feature.isEnabled || !hash) { return Promise.reject(); } return fetch(`${ feature.apiUrl }/${ hash }`, undefined, { omitSentryErrorLog: true }); }, - { - enabled: feature.isEnabled && Boolean(hash), - refetchOnMount: false, - }, - ); + enabled: feature.isEnabled && Boolean(hash), + refetchOnMount: false, + }); return Boolean(data); } diff --git a/package.json b/package.json index 732eec6b0d..20a9390eef 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,8 @@ "@monaco-editor/react": "^4.4.6", "@sentry/react": "^7.72.0", "@slise/embed-react": "^2.2.0", - "@tanstack/react-query": "^4.0.10", - "@tanstack/react-query-devtools": "^4.0.10", + "@tanstack/react-query": "^5.4.3", + "@tanstack/react-query-devtools": "^5.4.3", "@types/papaparse": "^5.3.5", "@types/react-scroll": "^1.8.4", "@web3modal/ethereum": "^2.6.2", @@ -89,6 +89,7 @@ "@playwright/experimental-ct-react": "1.35.1", "@playwright/test": "^1.35.1", "@svgr/webpack": "^6.5.1", + "@tanstack/eslint-plugin-query": "^5.0.5", "@testing-library/react": "^14.0.0", "@total-typescript/ts-reset": "^0.4.0", "@types/crypto-js": "^4.1.1", diff --git a/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx b/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx index ba5204f6a7..b1f6bf4aa8 100644 --- a/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx +++ b/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx @@ -57,7 +57,8 @@ const ApiKeyForm: React.FC = ({ data, onClose, setAlertVisible }) => { }); }; - const mutation = useMutation(updateApiKey, { + const mutation = useMutation({ + mutationFn: updateApiKey, onSuccess: async(data) => { const response = data as unknown as ApiKey; @@ -148,7 +149,7 @@ const ApiKeyForm: React.FC = ({ data, onClose, setAlertVisible }) => { size="lg" type="submit" isDisabled={ !isDirty } - isLoading={ mutation.isLoading } + isLoading={ mutation.isPending } > { data ? 'Save' : 'Generate API key' } diff --git a/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx b/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx index ed546cc9af..ed36d0f522 100644 --- a/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx +++ b/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx @@ -63,7 +63,8 @@ const CustomAbiForm: React.FC = ({ data, onClose, setAlertVisible }) => { const formBackgroundColor = useColorModeValue('white', 'gray.900'); - const mutation = useMutation(customAbiKey, { + const mutation = useMutation({ + mutationFn: customAbiKey, onSuccess: (data) => { const response = data as unknown as CustomAbi; queryClient.setQueryData([ resourceKey('custom_abi') ], (prevData: CustomAbis | undefined) => { @@ -175,7 +176,7 @@ const CustomAbiForm: React.FC = ({ data, onClose, setAlertVisible }) => { size="lg" type="submit" isDisabled={ !isDirty } - isLoading={ mutation.isLoading } + isLoading={ mutation.isPending } > { data ? 'Save' : 'Create custom ABI' } diff --git a/ui/marketplace/useMarketplaceApps.tsx b/ui/marketplace/useMarketplaceApps.tsx index f36bd3b322..0e07ca2893 100644 --- a/ui/marketplace/useMarketplaceApps.tsx +++ b/ui/marketplace/useMarketplaceApps.tsx @@ -24,15 +24,14 @@ function isAppCategoryMatches(category: string, app: MarketplaceAppOverview, fav export default function useMarketplaceApps(filter: string, selectedCategoryId: string = MarketplaceCategory.ALL, favoriteApps: Array = []) { const apiFetch = useApiFetch(); - const { isPlaceholderData, isError, error, data } = useQuery, Array>( - [ 'marketplace-apps' ], - async() => apiFetch(configUrl, undefined, { resource: 'marketplace-apps' }), - { - select: (data) => (data as Array).sort((a, b) => a.title.localeCompare(b.title)), - placeholderData: feature.isEnabled ? Array(9).fill(MARKETPLACE_APP) : undefined, - staleTime: Infinity, - enabled: feature.isEnabled, - }); + const { isPlaceholderData, isError, error, data } = useQuery, Array>({ + queryKey: [ 'marketplace-apps' ], + queryFn: async() => apiFetch(configUrl, undefined, { resource: 'marketplace-apps' }), + select: (data) => (data as Array).sort((a, b) => a.title.localeCompare(b.title)), + placeholderData: feature.isEnabled ? Array(9).fill(MARKETPLACE_APP) : undefined, + staleTime: Infinity, + enabled: feature.isEnabled, + }); const displayedApps = React.useMemo(() => { return data?.filter(app => isAppNameMatches(filter, app) && isAppCategoryMatches(selectedCategoryId, app, favoriteApps)) || []; diff --git a/ui/pages/MarketplaceApp.tsx b/ui/pages/MarketplaceApp.tsx index 7c6646beeb..c206c5c9f7 100644 --- a/ui/pages/MarketplaceApp.tsx +++ b/ui/pages/MarketplaceApp.tsx @@ -34,9 +34,9 @@ const MarketplaceApp = () => { const router = useRouter(); const id = getQueryParamString(router.query.id); - const { isLoading, isError, error, data } = useQuery, MarketplaceAppOverview>( - [ 'marketplace-apps', id ], - async() => { + const { isPending, isError, error, data } = useQuery, MarketplaceAppOverview>({ + queryKey: [ 'marketplace-apps', id ], + queryFn: async() => { const result = await apiFetch, unknown>(configUrl, undefined, { resource: 'marketplace-apps' }); if (!Array.isArray(result)) { throw result; @@ -49,12 +49,10 @@ const MarketplaceApp = () => { return item; }, - { - enabled: feature.isEnabled, - }, - ); + enabled: feature.isEnabled, + }); - const [ isFrameLoading, setIsFrameLoading ] = useState(isLoading); + const [ isFrameLoading, setIsFrameLoading ] = useState(isPending); const { colorMode } = useColorMode(); const handleIframeLoad = useCallback(() => { @@ -106,7 +104,7 @@ const MarketplaceApp = () => { return ( <> - { !isLoading && } + { !isPending && }
= ({ data, onClose, onSuccess, setAlertVisibl const formBackgroundColor = useColorModeValue('white', 'gray.900'); - const { mutate } = useMutation((formData: Inputs) => { - const body = { - name: formData?.tag, - address_hash: formData?.address, - }; + const { mutate } = useMutation({ + mutationFn: (formData: Inputs) => { + const body = { + name: formData?.tag, + address_hash: formData?.address, + }; - const isEdit = data?.id; - if (isEdit) { - return apiFetch('private_tags_address', { - pathParams: { id: data.id }, - fetchParams: { method: 'PUT', body }, - }); - } + const isEdit = data?.id; + if (isEdit) { + return apiFetch('private_tags_address', { + pathParams: { id: data.id }, + fetchParams: { method: 'PUT', body }, + }); + } - return apiFetch('private_tags_address', { fetchParams: { method: 'POST', body } }); - }, { + return apiFetch('private_tags_address', { fetchParams: { method: 'POST', body } }); + }, onError: (error: ResourceErrorAccount) => { setPending(false); const errorMap = error.payload?.errors; diff --git a/ui/privateTags/TransactionModal/TransactionForm.tsx b/ui/privateTags/TransactionModal/TransactionForm.tsx index 35097aeee3..dc3d11ac41 100644 --- a/ui/privateTags/TransactionModal/TransactionForm.tsx +++ b/ui/privateTags/TransactionModal/TransactionForm.tsx @@ -47,22 +47,23 @@ const TransactionForm: React.FC = ({ data, onClose, onSuccess, setAlertVi const queryClient = useQueryClient(); const apiFetch = useApiFetch(); - const { mutate } = useMutation((formData: Inputs) => { - const body = { - name: formData?.tag, - transaction_hash: formData?.transaction, - }; - const isEdit = data?.id; - - if (isEdit) { - return apiFetch('private_tags_tx', { - pathParams: { id: data.id }, - fetchParams: { method: 'PUT', body }, - }); - } - - return apiFetch('private_tags_tx', { fetchParams: { method: 'POST', body } }); - }, { + const { mutate } = useMutation({ + mutationFn: (formData: Inputs) => { + const body = { + name: formData?.tag, + transaction_hash: formData?.transaction, + }; + const isEdit = data?.id; + + if (isEdit) { + return apiFetch('private_tags_tx', { + pathParams: { id: data.id }, + fetchParams: { method: 'PUT', body }, + }); + } + + return apiFetch('private_tags_tx', { fetchParams: { method: 'POST', body } }); + }, onError: (error: ResourceErrorAccount) => { setPending(false); const errorMap = error.payload?.errors; @@ -76,7 +77,7 @@ const TransactionForm: React.FC = ({ data, onClose, onSuccess, setAlertVi } }, onSuccess: async() => { - await queryClient.refetchQueries([ resourceKey('private_tags_tx') ]); + await queryClient.refetchQueries({ queryKey: [ resourceKey('private_tags_tx') ] }); await onSuccess(); onClose(); setPending(false); diff --git a/ui/publicTags/PublicTagsForm/PublicTagsForm.tsx b/ui/publicTags/PublicTagsForm/PublicTagsForm.tsx index da25e86eae..07fb7ec2ae 100644 --- a/ui/publicTags/PublicTagsForm/PublicTagsForm.tsx +++ b/ui/publicTags/PublicTagsForm/PublicTagsForm.tsx @@ -109,7 +109,8 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { }); }; - const mutation = useMutation(updatePublicTag, { + const mutation = useMutation({ + mutationFn: updatePublicTag, onSuccess: async(data) => { const response = data as unknown as PublicTag; @@ -237,7 +238,7 @@ const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { size="lg" type="submit" isDisabled={ !isDirty } - isLoading={ mutation.isLoading } + isLoading={ mutation.isPending } > Send request diff --git a/ui/shared/DeleteModal.tsx b/ui/shared/DeleteModal.tsx index a6421c840c..67f055e2d4 100644 --- a/ui/shared/DeleteModal.tsx +++ b/ui/shared/DeleteModal.tsx @@ -39,7 +39,8 @@ const DeleteModal: React.FC = ({ onClose(); }, [ onClose, setAlertVisible ]); - const mutation = useMutation(mutationFn, { + const mutation = useMutation({ + mutationFn, onSuccess: async() => { onSuccess(); onClose(); @@ -70,7 +71,7 @@ const DeleteModal: React.FC = ({