Skip to content

Commit

Permalink
migrate to ReactQuery v5 (blockscout#1321)
Browse files Browse the repository at this point in the history
* change signature of hooks

* isLoading -> isPending

* default error type

* dev tools props
  • Loading branch information
tom2drum authored Nov 2, 2023
1 parent 3846c3b commit 139721e
Show file tree
Hide file tree
Showing 57 changed files with 285 additions and 244 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -31,6 +32,7 @@ module.exports = {
'eslint-plugin-import-helpers',
'jest',
'eslint-plugin-no-cyrillic-string',
'@tanstack/query',
],
parser: '@typescript-eslint/parser',
parserOptions: {
Expand Down
11 changes: 7 additions & 4 deletions lib/api/useApiQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ export default function useApiQuery<R extends ResourceName, E = unknown>(
) {
const apiFetch = useApiFetch();

return useQuery<ResourcePayload<R>, ResourceError<E>, ResourcePayload<R>>(
getResourceKey(resource, { pathParams, queryParams }),
async() => {
return useQuery<ResourcePayload<R>, ResourceError<E>, ResourcePayload<R>>({
// 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<ResourcePayload<R>>"
return apiFetch(resource, { pathParams, queryParams, fetchParams }) as Promise<ResourcePayload<R>>;
}, queryOptions);
},
...queryOptions,
});
}
2 changes: 1 addition & 1 deletion lib/api/useQueryClientConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function useQueryClientConfig() {
}
return failureCount < 2;
},
useErrorBoundary: (error) => {
throwOnError: (error) => {
const status = getErrorObjStatusCode(error);
// don't catch error for "Too many requests" response
return status === 429;
Expand Down
38 changes: 20 additions & 18 deletions lib/hooks/useGetCsrfToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
});
}
14 changes: 6 additions & 8 deletions lib/hooks/useIsSafeAddress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
"@sentry/cli": "^2.21.2",
"@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",
Expand Down Expand Up @@ -90,6 +90,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",
Expand Down
2 changes: 1 addition & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
{ getLayout(<Component { ...pageProps }/>) }
</SocketProvider>
</ScrollDirectionProvider>
<ReactQueryDevtools/>
<ReactQueryDevtools buttonPosition="bottom-left" position="left"/>
<GoogleAnalytics/>
</QueryClientProvider>
</AppContextProvider>
Expand Down
4 changes: 2 additions & 2 deletions ui/address/coinBalance/AddressCoinBalanceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface Props {
}

const AddressCoinBalanceChart = ({ addressHash }: Props) => {
const { data, isLoading, isError } = useApiQuery('address_coin_balance_chart', {
const { data, isPending, isError } = useApiQuery('address_coin_balance_chart', {
pathParams: { hash: addressHash },
});

Expand All @@ -24,7 +24,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
isError={ isError }
title="Balances"
items={ items }
isLoading={ isLoading }
isLoading={ isPending }
h="300px"
units={ config.chain.currency.symbol }
/>
Expand Down
3 changes: 2 additions & 1 deletion ui/address/coinBalance/AddressCoinBalanceHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { AddressCoinBalanceHistoryResponse } from 'types/api/address';
import type { PaginationParams } from 'ui/shared/pagination/types';

import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
Expand All @@ -15,7 +16,7 @@ import AddressCoinBalanceListItem from './AddressCoinBalanceListItem';
import AddressCoinBalanceTableItem from './AddressCoinBalanceTableItem';

interface Props {
query: UseQueryResult<AddressCoinBalanceHistoryResponse> & {
query: UseQueryResult<AddressCoinBalanceHistoryResponse, ResourceError<unknown>> & {
pagination: PaginationParams;
};
}
Expand Down
4 changes: 2 additions & 2 deletions ui/address/contract/ContractRead.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const ContractRead = ({ addressHash, isProxy, isCustomAbi }: Props) => {
const apiFetch = useApiFetch();
const account = useWatchAccount();

const { data, isLoading, isError } = useApiQuery(isProxy ? 'contract_methods_read_proxy' : 'contract_methods_read', {
const { data, isPending, isError } = useApiQuery(isProxy ? 'contract_methods_read_proxy' : 'contract_methods_read', {
pathParams: { hash: addressHash },
queryParams: {
is_custom_abi: isCustomAbi ? 'true' : 'false',
Expand Down Expand Up @@ -83,7 +83,7 @@ const ContractRead = ({ addressHash, isProxy, isCustomAbi }: Props) => {
return <DataFetchAlert/>;
}

if (isLoading) {
if (isPending) {
return <ContentLoader/>;
}

Expand Down
4 changes: 2 additions & 2 deletions ui/address/contract/ContractWrite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => {
const { chain } = useNetwork();
const { switchNetworkAsync } = useSwitchNetwork();

const { data, isLoading, isError } = useApiQuery(isProxy ? 'contract_methods_write_proxy' : 'contract_methods_write', {
const { data, isPending, isError } = useApiQuery(isProxy ? 'contract_methods_write_proxy' : 'contract_methods_write', {
pathParams: { hash: addressHash },
queryParams: {
is_custom_abi: isCustomAbi ? 'true' : 'false',
Expand Down Expand Up @@ -99,7 +99,7 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => {
return <DataFetchAlert/>;
}

if (isLoading) {
if (isPending) {
return <ContentLoader/>;
}

Expand Down
3 changes: 2 additions & 1 deletion ui/address/details/AddressCounterItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import type { AddressCounters } from 'types/api/address';

import { route } from 'nextjs-routes';

import type { ResourceError } from 'lib/api/resources';
import LinkInternal from 'ui/shared/LinkInternal';

interface Props {
prop: keyof AddressCounters;
query: UseQueryResult<AddressCounters>;
query: UseQueryResult<AddressCounters, ResourceError<unknown>>;
address: string;
onClick: () => void;
isAddressQueryLoading: boolean;
Expand Down
4 changes: 2 additions & 2 deletions ui/address/tokenSelect/TokenSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const TokenSelect = ({ onClick }: Props) => {

const addressQueryData = queryClient.getQueryData<Address>(addressResourceKey);

const { data, isError, isLoading, refetch } = useFetchTokens({ hash: addressQueryData?.hash });
const { data, isError, isPending, refetch } = useFetchTokens({ hash: addressQueryData?.hash });
const tokensResourceKey = getResourceKey('address_tokens', { pathParams: { hash: addressQueryData?.hash }, queryParams: { type: 'ERC-20' } });
const tokensIsFetching = useIsFetching({ queryKey: tokensResourceKey });

Expand Down Expand Up @@ -72,7 +72,7 @@ const TokenSelect = ({ onClick }: Props) => {
handler: handleTokenBalanceMessage,
});

if (isLoading) {
if (isPending) {
return (
<Flex columnGap={ 3 }>
<Skeleton h={ 8 } w="150px" borderRadius="base"/>
Expand Down
6 changes: 3 additions & 3 deletions ui/address/tokens/TokenBalances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,20 @@ const TokenBalances = () => {
<TokenBalancesItem
name="Net Worth"
value={ addressData?.exchange_rate ? `${ prefix }$${ totalUsd.toFormat(2) } USD` : 'N/A' }
isLoading={ addressQuery.isLoading || tokenQuery.isLoading }
isLoading={ addressQuery.isPending || tokenQuery.isPending }
/>
<TokenBalancesItem
name={ `${ config.chain.currency.symbol } Balance` }
value={ (!nativeUsd.eq(ZERO) ? `$${ nativeUsd.toFormat(2) } USD | ` : '') + `${ nativeValue } ${ config.chain.currency.symbol }` }
isLoading={ addressQuery.isLoading || tokenQuery.isLoading }
isLoading={ addressQuery.isPending || tokenQuery.isPending }
/>
<TokenBalancesItem
name="Tokens"
value={
`${ prefix }$${ tokensInfo.usd.toFormat(2) } USD ` +
tokensNumText
}
isLoading={ addressQuery.isLoading || tokenQuery.isLoading }
isLoading={ addressQuery.isPending || tokenQuery.isPending }
/>
</Flex>
);
Expand Down
2 changes: 1 addition & 1 deletion ui/address/utils/useFetchTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function useFetchTokens({ hash }: Props) {
}, [ erc1155query.data, erc20query.data, erc721query.data ]);

return {
isLoading: erc20query.isLoading || erc721query.isLoading || erc1155query.isLoading,
isPending: erc20query.isPending || erc721query.isPending || erc1155query.isPending,
isError: erc20query.isError || erc721query.isError || erc1155query.isError,
data,
refetch,
Expand Down
5 changes: 3 additions & 2 deletions ui/apiKey/ApiKeyModal/ApiKeyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
});
};

const mutation = useMutation(updateApiKey, {
const mutation = useMutation({
mutationFn: updateApiKey,
onSuccess: async(data) => {
const response = data as unknown as ApiKey;

Expand Down Expand Up @@ -148,7 +149,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
size="lg"
type="submit"
isDisabled={ !isDirty }
isLoading={ mutation.isLoading }
isLoading={ mutation.isPending }
>
{ data ? 'Save' : 'Generate API key' }
</Button>
Expand Down
6 changes: 3 additions & 3 deletions ui/block/BlockDetails.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const hooksConfig = {
test('regular block +@mobile +@dark-mode', async({ mount, page }) => {
const query = {
data: blockMock.base,
isLoading: false,
isPending: false,
} as UseQueryResult<Block, ResourceError>;

const component = await mount(
Expand All @@ -39,7 +39,7 @@ test('regular block +@mobile +@dark-mode', async({ mount, page }) => {
test('genesis block', async({ mount, page }) => {
const query = {
data: blockMock.genesis,
isLoading: false,
isPending: false,
} as UseQueryResult<Block, ResourceError>;

const component = await mount(
Expand All @@ -62,7 +62,7 @@ const customFieldsTest = test.extend({
customFieldsTest('rootstock custom fields', async({ mount, page }) => {
const query = {
data: blockMock.rootstock,
isLoading: false,
isPending: false,
} as UseQueryResult<Block, ResourceError>;

const component = await mount(
Expand Down
5 changes: 3 additions & 2 deletions ui/customAbi/CustomAbiModal/CustomAbiForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ const CustomAbiForm: React.FC<Props> = ({ 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) => {
Expand Down Expand Up @@ -175,7 +176,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
size="lg"
type="submit"
isDisabled={ !isDirty }
isLoading={ mutation.isLoading }
isLoading={ mutation.isPending }
>
{ data ? 'Save' : 'Create custom ABI' }
</Button>
Expand Down
4 changes: 2 additions & 2 deletions ui/home/indicators/ChainIndicatorChartContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import ChainIndicatorChart from './ChainIndicatorChart';

type Props = UseQueryResult<TimeChartData>;

const ChainIndicatorChartContainer = ({ data, isError, isLoading }: Props) => {
const ChainIndicatorChartContainer = ({ data, isError, isPending }: Props) => {

const content = (() => {
if (isLoading) {
if (isPending) {
return <ContentLoader mt="auto"/>;
}

Expand Down
5 changes: 3 additions & 2 deletions ui/home/indicators/ChainIndicatorItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React from 'react';
import type { HomeStats } from 'types/api/stats';
import type { ChainIndicatorId } from 'types/homepage';

import type { ResourceError } from 'lib/api/resources';
import useIsMobile from 'lib/hooks/useIsMobile';

interface Props {
Expand All @@ -14,7 +15,7 @@ interface Props {
icon: React.ReactNode;
isSelected: boolean;
onClick: (id: ChainIndicatorId) => void;
stats: UseQueryResult<HomeStats>;
stats: UseQueryResult<HomeStats, ResourceError<unknown>>;
}

const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick, stats }: Props) => {
Expand All @@ -33,7 +34,7 @@ const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick, stats
return null;
}

if (stats.isLoading) {
if (stats.isPending) {
return (
<Skeleton
h={ 3 }
Expand Down
2 changes: 1 addition & 1 deletion ui/home/indicators/ChainIndicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const ChainIndicators = () => {
}

const valueTitle = (() => {
if (statsQueryResult.isLoading) {
if (statsQueryResult.isPending) {
return <Skeleton h="48px" w="215px" mt={ 3 } mb={ 4 }/>;
}

Expand Down
Loading

0 comments on commit 139721e

Please sign in to comment.