diff --git a/src/containers/Accounts/AMM/AMMAccounts/index.tsx b/src/containers/Accounts/AMM/AMMAccounts/index.tsx index b3361d61a..4c8130dc6 100644 --- a/src/containers/Accounts/AMM/AMMAccounts/index.tsx +++ b/src/containers/Accounts/AMM/AMMAccounts/index.tsx @@ -1,4 +1,4 @@ -import { FC, PropsWithChildren, useContext, useEffect } from 'react' +import { FC, PropsWithChildren, useContext } from 'react' import { useQuery } from 'react-query' import { Helmet } from 'react-helmet-async' import { useLanguage } from '../../../shared/hooks' @@ -105,25 +105,25 @@ export const AMMAccounts = () => { }) }) - useEffect( - () => () => { - window.scrollTo(0, 0) + useQuery( + ['track-screen-load', data], + () => { + if (!data) { + return null + } + + trackScreenLoaded({ + account_id: data.accountId, + asset1: `${hexToString(data.balance.currency)}.${data.balance.issuer}`, + asset2: `${hexToString(data.balance2.currency)}.${data.balance2.issuer}`, + }) + return null + }, + { + enabled: !!data, }, - [], ) - useEffect(() => { - if (!data) { - return - } - - trackScreenLoaded({ - account_id: data.accountId, - asset1: `${hexToString(data.balance.currency)}.${data.balance.issuer}`, - asset2: `${hexToString(data.balance2.currency)}.${data.balance2.issuer}`, - }) - }, [data, trackScreenLoaded]) - const tabs = ['transactions'] if (error) { diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index dfdbd539d..cba86aa17 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -1,5 +1,6 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { Helmet } from 'react-helmet-async' +import { useQuery } from 'react-query' import { AccountHeader } from './AccountHeader' import { AccountTransactionTable } from './AccountTransactionTable' import './styles.scss' @@ -16,13 +17,11 @@ export const Accounts = () => { const [currencySelected, setCurrencySelected] = useState('XRP') const mainPath = buildPath(ACCOUNT_ROUTE, { id: accountId }) - useEffect(() => { + useQuery(['screen-load', tab], () => { trackScreenLoaded() - - return () => { - window.scrollTo(0, 0) - } - }, [tab, trackScreenLoaded]) + window.scrollTo(0, 0) + return null + }) const tabs = ['transactions', 'assets'] diff --git a/src/containers/CustomNetworkHome/index.tsx b/src/containers/CustomNetworkHome/index.tsx index a7fdbb062..769b5b89b 100644 --- a/src/containers/CustomNetworkHome/index.tsx +++ b/src/containers/CustomNetworkHome/index.tsx @@ -1,6 +1,7 @@ -import { KeyboardEvent, useEffect, useState } from 'react' +import { KeyboardEvent, useState } from 'react' import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' +import { useQuery } from 'react-query' import CustomNetworkLogo from '../shared/images/custom_network_logo.svg' import RightArrow from '../shared/images/side_arrow_green.svg' import { useAnalytics } from '../shared/analytics' @@ -14,9 +15,10 @@ const SidechainHome = () => { const [networkText, setNetworkText] = useState('') const [customNetworks = []] = useCustomNetworks() - useEffect(() => { + useQuery(['screen-load'], () => { trackScreenLoaded() - }, [trackScreenLoaded]) + return null + }) function switchMode(desiredLink: string) { const customNetworkUrl = process.env.VITE_CUSTOMNETWORK_LINK diff --git a/src/containers/Header/Search.tsx b/src/containers/Header/Search.tsx index 3b1032be3..32232ef2f 100644 --- a/src/containers/Header/Search.tsx +++ b/src/containers/Header/Search.tsx @@ -1,10 +1,4 @@ -import { - FC, - KeyboardEventHandler, - useContext, - useEffect, - useState, -} from 'react' +import { FC, KeyboardEventHandler, useContext, useState } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import { XrplClient } from 'xrpl-client' @@ -13,6 +7,7 @@ import { isValidXAddress, classicAddressToXAddress, } from 'ripple-address-codec' +import { useQuery } from 'react-query' import CloseIcon from '../shared/images/close.png' import { useAnalytics } from '../shared/analytics' @@ -177,8 +172,22 @@ export const Search = ({ callback = () => {} }: SearchProps) => { const { t } = useTranslation() const socket = useContext(SocketContext) const navigate = useNavigate() - const [currentSearchInput, setCurrentSearchInput] = useState('') + const [isBannerVisible, setIsBannerVisible] = useState(true) + + useQuery( + ['banner-timeout'], + () => { + const timeoutId = setTimeout(() => { + setIsBannerVisible(false) + }, 10000) // Disappear after 10 seconds + + return () => clearTimeout(timeoutId) + }, + { + enabled: isBannerVisible, + }, + ) const handleSearch = async (id: string) => { const strippedId = id.replace(/^["']|["']$/g, '') @@ -199,16 +208,6 @@ export const Search = ({ callback = () => {} }: SearchProps) => { } } - const [isBannerVisible, setIsBannerVisible] = useState(true) - - useEffect(() => { - const timeoutId = setTimeout(() => { - setIsBannerVisible(false) - }, 10000) // Disappear after 10 seconds - - return () => clearTimeout(timeoutId) - }, []) - return ( <> {process.env.VITE_ENVIRONMENT === 'mainnet' && isBannerVisible && ( diff --git a/src/containers/Ledger/index.tsx b/src/containers/Ledger/index.tsx index a1ec8eea6..b1290a5fa 100644 --- a/src/containers/Ledger/index.tsx +++ b/src/containers/Ledger/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from 'react' +import { useContext } from 'react' import { Helmet } from 'react-helmet-async' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' @@ -83,9 +83,10 @@ export const Ledger = () => { ) }) - useEffect(() => { + useQuery(['screen-load'], () => { trackScreenLoaded() - }, [trackScreenLoaded]) + return null + }) const renderNav = (data: any) => { const { ledger_index: LedgerIndex, ledger_hash: LedgerHash } = data diff --git a/src/containers/Ledgers/Legend.tsx b/src/containers/Ledgers/Legend.tsx index 33922b634..71753beee 100644 --- a/src/containers/Ledgers/Legend.tsx +++ b/src/containers/Ledgers/Legend.tsx @@ -1,6 +1,7 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useWindowSize } from 'usehooks-ts' +import { useQuery } from 'react-query' import { TransactionAction, TransactionCategory, @@ -19,11 +20,12 @@ export const Legend = () => { const [hidden, setHidden] = useState(previousInteraction) // Show legend by default when on desktop sizes - useEffect(() => { + useQuery(['legend-visibility', windowSize.width, previousInteraction], () => { if (previousInteraction === false) { setHidden(!(windowSize.width > 900)) } - }, [previousInteraction, windowSize]) + return null + }) const actions = [ TransactionAction.CREATE, diff --git a/src/containers/Ledgers/index.tsx b/src/containers/Ledgers/index.tsx index 3dfe9c9f0..30ef64aa8 100644 --- a/src/containers/Ledgers/index.tsx +++ b/src/containers/Ledgers/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from 'react' +import { useContext, useState } from 'react' import { Helmet } from 'react-helmet-async' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' @@ -30,12 +30,11 @@ export const LedgersPage = () => { const { t } = useTranslation() const network = useContext(NetworkContext) - useEffect(() => { + useQuery(['screen-load'], () => { trackScreenLoaded() - return () => { - window.scrollTo(0, 0) - } - }, [trackScreenLoaded]) + window.scrollTo(0, 0) + return null + }) const fetchValidators = () => { const url = `${process.env.VITE_DATA_URL}/validators/${network}` diff --git a/src/containers/MPT/MPT.tsx b/src/containers/MPT/MPT.tsx index 83e55e443..39c95f87f 100644 --- a/src/containers/MPT/MPT.tsx +++ b/src/containers/MPT/MPT.tsx @@ -1,6 +1,7 @@ -import { FC, PropsWithChildren, useEffect, useState } from 'react' +import { FC, PropsWithChildren, useState } from 'react' import { useParams } from 'react-router' import { Helmet } from 'react-helmet-async' +import { useQuery } from 'react-query' import NoMatch from '../NoMatch' import { MPTHeader } from './MPTHeader/MPTHeader' import { useAnalytics } from '../shared/analytics' @@ -41,14 +42,13 @@ export const MPT = () => { const { id: tokenId = '' } = useParams<{ id: string }>() const [error, setError] = useState(null) - useEffect(() => { + useQuery(['screen-load', tokenId], () => { trackScreenLoaded({ mpt_issuance_id: tokenId, }) - return () => { - window.scrollTo(0, 0) - } - }, [tokenId, trackScreenLoaded]) + window.scrollTo(0, 0) + return null + }) const renderError = () => { const message = getErrorMessage(error) diff --git a/src/containers/MPT/MPTHeader/MPTHeader.tsx b/src/containers/MPT/MPTHeader/MPTHeader.tsx index 354268991..b67c45809 100644 --- a/src/containers/MPT/MPTHeader/MPTHeader.tsx +++ b/src/containers/MPT/MPTHeader/MPTHeader.tsx @@ -1,4 +1,4 @@ -import { useEffect, useContext, useState } from 'react' +import { useContext, useState } from 'react' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' import { Loader } from '../../shared/components/Loader' @@ -40,11 +40,12 @@ export const MPTHeader = (props: Props) => { }, ) - useEffect(() => { + useQuery(['validate-token-id', tokenId], () => { if (!HASH192_REGEX.test(tokenId)) { setError(BAD_REQUEST) } - }, [setError, tokenId]) + return null + }) const showTooltip = (event: any, d: any) => { setTooltip({ diff --git a/src/containers/NFT/NFT.tsx b/src/containers/NFT/NFT.tsx index 3cb677ddd..014b06fc7 100644 --- a/src/containers/NFT/NFT.tsx +++ b/src/containers/NFT/NFT.tsx @@ -1,6 +1,7 @@ -import { FC, PropsWithChildren, useEffect, useState } from 'react' +import { FC, PropsWithChildren, useState } from 'react' import { useParams } from 'react-router' import { Helmet } from 'react-helmet-async' +import { useQuery } from 'react-query' import NoMatch from '../NoMatch' import { NFTHeader } from './NFTHeader/NFTHeader' import { NFTTabs } from './NFTTabs/NFTTabs' @@ -43,15 +44,14 @@ export const NFT = () => { const { id: tokenId = '' } = useParams<{ id: string }>() const [error, setError] = useState(null) - useEffect(() => { + useQuery(['screen-load', tokenId], () => { trackScreenLoaded({ nftoken_id: tokenId, issuer: parseIssuerFromNFTokenID(tokenId), }) - return () => { - window.scrollTo(0, 0) - } - }, [tokenId, trackScreenLoaded]) + window.scrollTo(0, 0) + return null + }) const renderError = () => { const message = getErrorMessage(error) diff --git a/src/containers/NFT/NFTHeader/NFTHeader.tsx b/src/containers/NFT/NFTHeader/NFTHeader.tsx index d53e2c233..699bcb305 100644 --- a/src/containers/NFT/NFTHeader/NFTHeader.tsx +++ b/src/containers/NFT/NFTHeader/NFTHeader.tsx @@ -1,4 +1,4 @@ -import { useEffect, useContext, useState } from 'react' +import { useContext, useState } from 'react' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' import { Loader } from '../../shared/components/Loader' @@ -55,11 +55,12 @@ export const NFTHeader = (props: Props) => { }, ) - useEffect(() => { + useQuery(['validate-token-id', tokenId], () => { if (!HASH256_REGEX.test(tokenId)) { setError(BAD_REQUEST) } - }, [setError, tokenId]) + return null + }) // fetch the oldest NFT transaction to get its minted data const { data: firstTransaction } = useQuery( diff --git a/src/containers/Network/Hexagons.jsx b/src/containers/Network/Hexagons.jsx index e351a3e9b..f17c7e622 100644 --- a/src/containers/Network/Hexagons.jsx +++ b/src/containers/Network/Hexagons.jsx @@ -1,6 +1,7 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import PropTypes from 'prop-types' import { useWindowSize } from 'usehooks-ts' +import { useQuery } from 'react-query' import { hexbin } from 'd3-hexbin' import { Loader } from '../shared/components/Loader' @@ -60,13 +61,14 @@ export const Hexagons = ({ list, data }) => { ]) .radius(radius) - useEffect(() => { + useQuery(['prepare-hexagons', data, list, width, gridHeight, radius], () => { if (width > 0) { setHexagons((prevHexagons) => prepareHexagons(data, list, gridHeight, radius, prevHexagons), ) } - }, [data, list, width, gridHeight, radius]) + return null + }) const renderHexagon = (d, theHex) => { const { cookie, pubkey, ledger_hash: ledgerHash } = d diff --git a/src/containers/Network/index.tsx b/src/containers/Network/index.tsx index 70888bc9d..6f23ec0fc 100644 --- a/src/containers/Network/index.tsx +++ b/src/containers/Network/index.tsx @@ -1,6 +1,7 @@ -import { useContext, useEffect } from 'react' +import { useContext } from 'react' import { useTranslation } from 'react-i18next' import { Helmet } from 'react-helmet-async' +import { useQuery } from 'react-query' import { NETWORK_ROUTE } from '../App/routes' import { useAnalytics } from '../shared/analytics' import { useRouteParams } from '../shared/routing' @@ -17,9 +18,10 @@ export const Network = () => { const { tab = 'nodes' } = useRouteParams(NETWORK_ROUTE) const network = useContext(NetworkContext) - useEffect(() => { + useQuery(['screen-load', tab], () => { trackScreenLoaded() - }, [tab, trackScreenLoaded]) + return null + }) if (network === null) { return ( diff --git a/src/containers/NoMatch/index.tsx b/src/containers/NoMatch/index.tsx index 5d1e7ad0a..7191f3902 100644 --- a/src/containers/NoMatch/index.tsx +++ b/src/containers/NoMatch/index.tsx @@ -1,6 +1,7 @@ -import { useContext, useEffect } from 'react' +import { useContext } from 'react' import { Helmet } from 'react-helmet-async' import { useTranslation } from 'react-i18next' +import { useQuery } from 'react-query' import { useAnalytics } from '../shared/analytics' import SocketContext from '../shared/SocketContext' import InfoIcon from '../shared/images/info.svg' @@ -32,12 +33,13 @@ const NoMatch = ({ const socket = useContext(SocketContext) const values = { connection: socket?.getState() } - useEffect(() => { + useQuery(['track-not-found', ...hints, title], () => { track('not_found', { description: `${title} -- ${hints.join(', ')}`, }) // eslint-disable-next-line react-hooks/exhaustive-deps -- hints has to be spread to prevent this from running multiple times - }, [...hints, title, track]) + return null + }) const notFound = title.includes('not_found') const hintMsg = hints.map((hint) => ( diff --git a/src/containers/PayStrings/index.tsx b/src/containers/PayStrings/index.tsx new file mode 100644 index 000000000..642091d75 --- /dev/null +++ b/src/containers/PayStrings/index.tsx @@ -0,0 +1,60 @@ +import { Helmet } from 'react-helmet-async' +import { useParams } from 'react-router' +import { useQuery } from 'react-query' + +import { useAnalytics } from '../shared/analytics' +import { PayStringHeader } from './PayStringHeader' +import { PayStringMappingsTable } from './PayStringMappingsTable' +import NoMatch from '../NoMatch' + +import './styles.scss' +import { getPayString } from '../../rippled' + +export const PayString = () => { + const { id: accountId = '' } = useParams<{ id: string }>() + const { trackException, trackScreenLoaded } = useAnalytics() + + const { data, isError, isLoading } = useQuery(['paystring', accountId], () => + getPayString(accountId).catch((transactionRequestError) => { + const status = transactionRequestError.code + + trackException( + `PayString ${accountId} --- ${JSON.stringify(transactionRequestError)}`, + ) + return Promise.reject(status) + }), + ) + + useQuery(['screen-load', accountId], () => { + trackScreenLoaded() + window.scrollTo(0, 0) + return null + }) + + const renderError = () => ( +
+ +
+ ) + + return isError ? ( + renderError() + ) : ( +
+ 24 ? '...' : '' + }`} + /> + {accountId && } + {accountId && } + {!accountId && ( + + )} +
+ ) +} diff --git a/src/containers/Token/index.tsx b/src/containers/Token/index.tsx index 689475003..13d542538 100644 --- a/src/containers/Token/index.tsx +++ b/src/containers/Token/index.tsx @@ -1,4 +1,4 @@ -import { FC, PropsWithChildren, useContext, useEffect } from 'react' +import { FC, PropsWithChildren, useContext } from 'react' import { useTranslation } from 'react-i18next' import { Helmet } from 'react-helmet-async' @@ -60,16 +60,14 @@ export const Token = () => { queryFn: () => getToken(currency, accountId, rippledSocket), }) - useEffect(() => { + useQuery(['screen-load', accountId, currency], () => { trackScreenLoaded({ issuer: accountId, currency_code: currency, }) - - return () => { - window.scrollTo(0, 0) - } - }, [accountId, currency, trackScreenLoaded]) + window.scrollTo(0, 0) + return null + }) const renderError = () => { const message = getErrorMessage(tokenDataError) diff --git a/src/containers/Transactions/index.tsx b/src/containers/Transactions/index.tsx index 2918e7674..f187d88b6 100644 --- a/src/containers/Transactions/index.tsx +++ b/src/containers/Transactions/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from 'react' +import { useContext } from 'react' import { Helmet } from 'react-helmet-async' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' @@ -79,24 +79,31 @@ export const Transaction = () => { ) const { width } = useWindowSize() - useEffect(() => { - if (!data?.processed) return + useQuery( + ['transaction-screen-load', identifier, data?.processed, tab], + () => { + if (!data?.processed) return null - const type = data?.processed.tx.TransactionType - const status = data?.processed.meta.TransactionResult + const type = data.processed.tx.TransactionType + const status = data.processed.meta.TransactionResult - const transactionProperties: AnalyticsFields = { - transaction_action: getAction(type), - transaction_category: getCategory(type), - transaction_type: type, - } + const transactionProperties: AnalyticsFields = { + transaction_action: getAction(type), + transaction_category: getCategory(type), + transaction_type: type, + } - if (status !== SUCCESSFUL_TRANSACTION) { - transactionProperties.tec_code = status - } + if (status !== SUCCESSFUL_TRANSACTION) { + transactionProperties.tec_code = status + } - trackScreenLoaded(transactionProperties) - }, [identifier, data?.processed, tab, trackScreenLoaded]) + trackScreenLoaded(transactionProperties) + return null + }, + { + enabled: !!data?.processed, + }, + ) function renderSummary() { const type = data?.processed.tx.TransactionType diff --git a/src/containers/Validators/index.tsx b/src/containers/Validators/index.tsx index 0a035ba14..5ced472f2 100644 --- a/src/containers/Validators/index.tsx +++ b/src/containers/Validators/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from 'react' +import { useContext } from 'react' import axios from 'axios' import { useTranslation } from 'react-i18next' import { useQuery } from 'react-query' @@ -76,9 +76,10 @@ export const Validator = () => { }, ) - useEffect(() => { + useQuery(['screen-load', identifier, tab], () => { trackScreenLoaded({ validator: identifier }) - }, [identifier, tab, trackScreenLoaded]) + return null + }) function fetchValidatorReport(): Promise { return axios diff --git a/src/containers/shared/NetworkContext.tsx b/src/containers/shared/NetworkContext.tsx index c14e16e39..4ed2b9e6e 100644 --- a/src/containers/shared/NetworkContext.tsx +++ b/src/containers/shared/NetworkContext.tsx @@ -1,5 +1,6 @@ -import React, { useEffect, useState } from 'react' +import React, { useState } from 'react' import axios from 'axios' +import { useQuery } from 'react-query' import Log from './log' const ENV_NETWORK_MAP: Record = { @@ -34,19 +35,27 @@ export const NetworkProvider = ({ const initialNetworkName = getNetworkName() const [networkName, setNetworkName] = useState(initialNetworkName) - useEffect(() => { - if (initialNetworkName == null && rippledUrl) { - axios - .get(`${process.env.VITE_DATA_URL}/get_network/${rippledUrl}`) - .then((resp) => resp.data) - .then((data) => + useQuery( + ['get-network', initialNetworkName, rippledUrl], + async () => { + if (initialNetworkName == null && rippledUrl) { + try { + const { data } = await axios.get( + `${process.env.VITE_DATA_URL}/get_network/${rippledUrl}`, + ) setNetworkName( data.result && data.result === 'error' ? null : data.network, - ), - ) - .catch((e) => Log.error(e)) - } - }, [initialNetworkName, rippledUrl]) + ) + } catch (e) { + Log.error(e) + } + } + return null + }, + { + enabled: initialNetworkName == null && !!rippledUrl, + }, + ) return ( diff --git a/src/containers/shared/analytics.ts b/src/containers/shared/analytics.ts index 305c43521..e98ca43ec 100644 --- a/src/containers/shared/analytics.ts +++ b/src/containers/shared/analytics.ts @@ -1,5 +1,6 @@ -import { useCallback, useEffect } from 'react' +import { useCallback } from 'react' import { useLocation } from 'react-router' +import { useQuery } from 'react-query' /* eslint-disable camelcase -- GA uses underscores for the names */ export type AnalyticsEventNames = @@ -95,7 +96,8 @@ export const useAnalytics = () => { export const AnalyticsSetPath = () => { const { setGlobals } = useAnalytics() const { hash, pathname, search } = useLocation() - useEffect(() => { + + useQuery(['analytics-set-path', hash, pathname, search], () => { // remove the custom mode's endpoint from the url path const url = (process.env.VITE_ENVIRONMENT === 'custom' @@ -107,7 +109,8 @@ export const AnalyticsSetPath = () => { setGlobals({ page_path: url, }) - }, [hash, pathname, search, setGlobals]) + return null + }) return null } diff --git a/src/containers/shared/components/Dropdown/Dropdown.tsx b/src/containers/shared/components/Dropdown/Dropdown.tsx index b525aa47d..fa76ab86a 100644 --- a/src/containers/shared/components/Dropdown/Dropdown.tsx +++ b/src/containers/shared/components/Dropdown/Dropdown.tsx @@ -1,5 +1,6 @@ import classnames from 'classnames' -import { ReactNode, useCallback, useEffect, useRef, useState } from 'react' +import { ReactNode, useCallback, useRef, useState } from 'react' +import { useQuery } from 'react-query' import ArrowIcon from '../../images/down_arrow.svg' import './dropdown.scss' @@ -43,12 +44,10 @@ export const Dropdown = ({ document.removeEventListener('click', globalClickListener) }, []) - useEffect( - (): (() => void) => () => - // remove listener when cleaning up component - document.removeEventListener('click', globalClickListener), - [globalClickListener], - ) + useQuery(['dropdown-cleanup', globalClickListener], () => () => { + // remove listener when cleaning up component + document.removeEventListener('click', globalClickListener) + }) const toggleExpand = () => { // don't de-expand if clicking in the textbox diff --git a/src/containers/shared/components/Notification/index.tsx b/src/containers/shared/components/Notification/index.tsx index 5ebc5ec92..b38318706 100755 --- a/src/containers/shared/components/Notification/index.tsx +++ b/src/containers/shared/components/Notification/index.tsx @@ -1,4 +1,5 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' +import { useQuery } from 'react-query' import './styles.scss' type NotificationLevel = 'primary' @@ -31,13 +32,21 @@ export const Notification = ({ className, }: NotificationProps) => { const [dismissed, setDismissed] = useState(false) - useEffect(() => { - if (autoDismiss) { - setTimeout(() => { - setDismissed(true) - }, delay) - } - }, [autoDismiss, delay]) + + useQuery( + ['notification-auto-dismiss', autoDismiss, delay], + () => { + if (autoDismiss) { + setTimeout(() => { + setDismissed(true) + }, delay) + } + return null + }, + { + cacheTime: 0, + }, + ) const classNames = ['notification', usage, `${level}-theme`, className].join( ' ', diff --git a/src/containers/shared/components/Transaction/EnableAmendment/Simple.tsx b/src/containers/shared/components/Transaction/EnableAmendment/Simple.tsx index d42416872..fce2a5bf3 100644 --- a/src/containers/shared/components/Transaction/EnableAmendment/Simple.tsx +++ b/src/containers/shared/components/Transaction/EnableAmendment/Simple.tsx @@ -1,5 +1,6 @@ -import { useContext, useEffect, useState } from 'react' +import { useContext, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useQuery } from 'react-query' import type { EnableAmendment } from 'xrpl' import { useLanguage } from '../../../hooks' import { SimpleRow } from '../SimpleRow' @@ -24,19 +25,22 @@ export const Simple = ({ data }: TransactionSimpleProps) => { }) const rippledSocket = useContext(SocketContext) - useEffect(() => { - const amendmentId = data.instructions.Amendment - getFeature(rippledSocket, amendmentId).then((feature) => { + useQuery( + ['amendment-details', data.instructions.Amendment, rippledSocket], + async () => { + const amendmentId = data.instructions.Amendment + const feature = await getFeature(rippledSocket, amendmentId) const name = feature && feature[amendmentId] ? feature[amendmentId].name : '' - getRippledVersion(name).then((rippledVersion) => { - setAmendmentDetails({ - name: name || states.unknown, - minRippledVersion: rippledVersion || states.unknown, - }) + const rippledVersion = await getRippledVersion(name) + + setAmendmentDetails({ + name: name || states.unknown, + minRippledVersion: rippledVersion || states.unknown, }) - }) - }, [data.instructions.Amendment, rippledSocket]) + return null + }, + ) let amendmentStatus = states.unknown let expectedDate: string | null = states.unknown diff --git a/src/containers/shared/hooks/useLocalStorage.ts b/src/containers/shared/hooks/useLocalStorage.ts index 58239b1f2..a015db7eb 100644 --- a/src/containers/shared/hooks/useLocalStorage.ts +++ b/src/containers/shared/hooks/useLocalStorage.ts @@ -1,4 +1,5 @@ -import { useState, useEffect } from 'react' +import { useState } from 'react' +import { useQuery } from 'react-query' function getStorageValue(key: string, defaultValue: T): T | undefined { // getting stored value @@ -15,10 +16,11 @@ export const useLocalStorage = ( getStorageValue(key, defaultValue), ) - useEffect(() => { + useQuery(['local-storage', key, value], () => { // storing input name localStorage.setItem(key, JSON.stringify(value)) - }, [key, value]) + return null + }) return [value, setValue] } diff --git a/src/index.tsx b/src/index.tsx index 36c5d4778..b8b22fe3b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,19 +2,32 @@ import { Suspense } from 'react' import ReactDOM from 'react-dom' import { BrowserRouter as Router } from 'react-router-dom' import { I18nextProvider } from 'react-i18next' +import { QueryClient, QueryClientProvider } from 'react-query' import { unregister } from './registerServiceWorker' import './containers/shared/css/global.scss' import { AppWrapper } from './containers/App' import i18n from './i18n' +// Create a client +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: false, + }, + }, +}) + const renderApp = () => { ReactDOM.render( - - - - - + + + + + + + , document.getElementById('xrpl-explorer'), )