From 525e312f9eefb9f200486026896ef402cbdd48e6 Mon Sep 17 00:00:00 2001 From: Deeptanshu-sankhwar Date: Tue, 11 Mar 2025 19:28:20 -0600 Subject: [PATCH 1/4] replace uses of useEffect with useQuery --- .../Accounts/AMM/AMMAccounts/index.tsx | 34 +++++++++--------- .../Accounts/AccountHeader/index.tsx | 17 ++++++--- src/containers/Accounts/index.tsx | 13 ++++--- src/containers/CustomNetworkHome/index.tsx | 8 +++-- src/containers/Header/Search.tsx | 35 +++++++++---------- src/containers/Ledger/index.tsx | 7 ++-- src/containers/Ledgers/Legend.tsx | 8 +++-- src/containers/Ledgers/index.tsx | 11 +++--- src/containers/MPT/MPT.tsx | 12 +++---- src/containers/MPT/MPTHeader/MPTHeader.tsx | 7 ++-- src/containers/NFT/NFT.tsx | 12 +++---- src/containers/NFT/NFTHeader/NFTHeader.tsx | 7 ++-- src/containers/Network/Hexagons.jsx | 8 +++-- src/containers/Network/index.tsx | 8 +++-- src/containers/NoMatch/index.tsx | 8 +++-- src/containers/PayStrings/index.tsx | 11 +++--- src/containers/Token/index.tsx | 12 +++---- src/containers/Validators/index.tsx | 7 ++-- src/containers/shared/NetworkContext.tsx | 34 +++++++++++------- src/containers/shared/analytics.ts | 9 +++-- .../shared/components/Dropdown/Dropdown.tsx | 13 +++---- .../shared/components/Notification/index.tsx | 25 ++++++++----- .../Transaction/EnableAmendment/Simple.tsx | 26 ++++++++------ .../shared/hooks/useLocalStorage.ts | 8 +++-- src/index.tsx | 19 ++++++++-- 25 files changed, 208 insertions(+), 151 deletions(-) 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/AccountHeader/index.tsx b/src/containers/Accounts/AccountHeader/index.tsx index 3c04aff64..1dc3a6122 100644 --- a/src/containers/Accounts/AccountHeader/index.tsx +++ b/src/containers/Accounts/AccountHeader/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from 'react' +import { useContext } from 'react' import { Trans, useTranslation } from 'react-i18next' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' @@ -13,6 +13,7 @@ import InfoIcon from '../../shared/images/info.svg' import { useLanguage } from '../../shared/hooks' import Currency from '../../shared/components/Currency' import DomainLink from '../../shared/components/DomainLink' +import { useQuery } from 'react-query' const CURRENCY_OPTIONS = { style: 'currency', @@ -83,9 +84,17 @@ const AccountHeader = (props: AccountHeaderProps) => { loading, } = props const { deleted } = data - useEffect(() => { - actions.loadAccountState(accountId, rippledSocket) - }, [accountId, actions, rippledSocket]) + + useQuery( + ['account-state', accountId], + () => { + actions.loadAccountState(accountId, rippledSocket) + return null + }, + { + enabled: !!accountId && !!rippledSocket, + }, + ) function renderBalancesSelector() { const { balances = {} } = data diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index 8f6361d6b..0236e36bc 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { Helmet } from 'react-helmet-async' import AccountHeader from './AccountHeader' import { AccountTransactionTable } from './AccountTransactionTable' @@ -8,6 +8,7 @@ import { Tabs } from '../shared/components/Tabs' import { AccountAssetTab } from './AccountAssetTab/AccountAssetTab' import { buildPath, useRouteParams } from '../shared/routing' import { ACCOUNT_ROUTE } from '../App/routes' +import { useQuery } from 'react-query' export const Accounts = () => { const { trackScreenLoaded } = useAnalytics() @@ -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..075525da3 100644 --- a/src/containers/CustomNetworkHome/index.tsx +++ b/src/containers/CustomNetworkHome/index.tsx @@ -1,4 +1,4 @@ -import { KeyboardEvent, useEffect, useState } from 'react' +import { KeyboardEvent, useState } from 'react' import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import CustomNetworkLogo from '../shared/images/custom_network_logo.svg' @@ -7,6 +7,7 @@ import { useAnalytics } from '../shared/analytics' import './index.scss' import { useCustomNetworks } from '../shared/hooks' import { Header } from '../Header' +import { useQuery } from 'react-query' const SidechainHome = () => { const { track, trackScreenLoaded } = useAnalytics() @@ -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 f1c0e8376..a75246f83 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' @@ -41,6 +35,7 @@ import { MPT_ROUTE, } from '../App/routes' import TokenSearchResults from '../shared/components/TokenSearchResults/TokenSearchResults' +import { useQuery } from 'react-query' const determineHashType = async (id: string, rippledContext: XrplClient) => { try { @@ -190,8 +185,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, '') @@ -212,16 +221,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 d4a408fbc..b310b416d 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, @@ -20,11 +21,12 @@ export const Legend = () => { // TODO: use global variables when we update places using width from redux. // 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 index 46845aa8f..642091d75 100644 --- a/src/containers/PayStrings/index.tsx +++ b/src/containers/PayStrings/index.tsx @@ -1,4 +1,3 @@ -import { useEffect } from 'react' import { Helmet } from 'react-helmet-async' import { useParams } from 'react-router' import { useQuery } from 'react-query' @@ -26,13 +25,11 @@ export const PayString = () => { }), ) - useEffect(() => { + useQuery(['screen-load', accountId], () => { trackScreenLoaded() - - return () => { - window.scrollTo(0, 0) - } - }, [accountId, trackScreenLoaded]) + window.scrollTo(0, 0) + return null + }) const renderError = () => (
diff --git a/src/containers/Token/index.tsx b/src/containers/Token/index.tsx index 00362cf0e..7d5d4f66f 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' @@ -63,16 +63,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/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..709f59166 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,28 @@ 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 resp = await axios.get( + `${process.env.VITE_DATA_URL}/get_network/${rippledUrl}`, + ) + const data = resp.data 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..86ce10bb0 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,12 @@ export const Dropdown = ({ document.removeEventListener('click', globalClickListener) }, []) - useEffect( - (): (() => void) => () => + useQuery(['dropdown-cleanup', globalClickListener], () => { + return () => { // remove listener when cleaning up component - document.removeEventListener('click', globalClickListener), - [globalClickListener], - ) + 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 cd53f50b8..2ba1b71de 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 { useLanguage } from '../../../hooks' import { SimpleRow } from '../SimpleRow' import { TransactionSimpleProps } from '../types' @@ -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 5eff37e4d..ef91d2851 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,6 +7,7 @@ import thunk from 'redux-thunk' import { I18nextProvider } from 'react-i18next' import reduxLogger from 'redux-logger' import { composeWithDevTools } from '@redux-devtools/extension' +import { QueryClient, QueryClientProvider } from 'react-query' import rootReducer from './rootReducer' import { unregister } from './registerServiceWorker' import './containers/shared/css/global.scss' @@ -16,14 +17,26 @@ import i18n from './i18n' let enhancers let store +// Create a client +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: false, + }, + }, +}) + const renderApp = () => { ReactDOM.render( - - - + + + + + , From 954d8f29d043f5d5972b4f29efaa0c01568e0cb8 Mon Sep 17 00:00:00 2001 From: Deeptanshu-sankhwar Date: Wed, 12 Mar 2025 07:20:58 -0600 Subject: [PATCH 2/4] more files --- src/containers/Token/DEXPairs/index.tsx | 52 ++++++++++++++----------- src/containers/Transactions/index.tsx | 37 +++++++++++------- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/containers/Token/DEXPairs/index.tsx b/src/containers/Token/DEXPairs/index.tsx index 84d6fd8bd..b7b79d738 100644 --- a/src/containers/Token/DEXPairs/index.tsx +++ b/src/containers/Token/DEXPairs/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useContext } from 'react' +import { useState, useRef, useContext } from 'react' import { useTranslation } from 'react-i18next' import axios from 'axios' import './styles.scss' @@ -16,6 +16,7 @@ import Currency from '../../shared/components/Currency' import { Pair } from './types' import { TOKEN_ROUTE } from '../../App/routes' import { RouteLink } from '../../shared/routing' +import { useQuery } from 'react-query' // Hard Coded Pairs that we always check for const pairsHardCoded = [ @@ -38,20 +39,20 @@ export const DEXPairs = ({ accountId, currency }: DexPairsProps) => { const { t } = useTranslation() const rippledSocket = useContext(SocketContext) - useEffect(() => { - isMountedRef.current = true - const promises: Promise[] = [] - axios - .get(`/api/v1/token/top`) - .then((tokenRes) => { + useQuery( + ['dex-pairs', accountId, currency], + async () => { + isMountedRef.current = true + const promises: Promise[] = [] + + try { + const tokenRes = await axios.get(`/api/v1/token/top`) let tokenList = tokenRes.data?.tokens if (tokenList) { tokenList = tokenList.map((element) => ({ currency: element.currency, issuer: element.issuer, })) - // Limit to top 20 tokens so that page in reasonable amount of time. - // TODO: add "Load more pairs feature" tokenList = tokenList.slice(0, 20) } else { tokenList = [] @@ -67,7 +68,6 @@ export const DEXPairs = ({ accountId, currency }: DexPairsProps) => { ) ) { promises.push( - // currency.accountId = "taker gets" token = "taker pays" pairs are "taker pays"/ "taker gets" getOffers( currency, accountId, @@ -116,24 +116,32 @@ export const DEXPairs = ({ accountId, currency }: DexPairsProps) => { ) } } - Promise.allSettled(promises).then(() => { - if (isMountedRef.current) { - setIsLoading(false) - } - }) - }) - .catch((err) => { + + await Promise.allSettled(promises) + if (isMountedRef.current) { + setIsLoading(false) + } + } catch (err) { if (isMountedRef.current) { setIsError(true) setIsLoading(false) } Log.error(err) trackException(`Error getting top tokens from /api/v1/tokens/top`) - }) - return () => { - isMountedRef.current = false - } - }, [accountId, currency, rippledSocket, trackException]) + } + + return () => { + isMountedRef.current = false + } + }, + { + onSettled: () => { + if (isMountedRef.current) { + setIsLoading(false) + } + }, + }, + ) function renderNoPairs() { return
{t('no_pairs_message')}
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 From 8b02ee8dc4612669c65a3ba0991134c80b210752 Mon Sep 17 00:00:00 2001 From: Deeptanshu-sankhwar Date: Fri, 11 Apr 2025 19:57:05 -0600 Subject: [PATCH 3/4] fix lint checks --- src/containers/Accounts/AccountHeader/index.tsx | 2 +- src/containers/Accounts/index.tsx | 2 +- src/containers/CustomNetworkHome/index.tsx | 2 +- src/containers/Header/Search.tsx | 2 +- src/containers/Token/DEXPairs/index.tsx | 2 +- src/containers/shared/NetworkContext.tsx | 3 +-- src/containers/shared/components/Dropdown/Dropdown.tsx | 8 +++----- 7 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/containers/Accounts/AccountHeader/index.tsx b/src/containers/Accounts/AccountHeader/index.tsx index 1dc3a6122..7ce3d4e5c 100644 --- a/src/containers/Accounts/AccountHeader/index.tsx +++ b/src/containers/Accounts/AccountHeader/index.tsx @@ -2,6 +2,7 @@ import { useContext } from 'react' import { Trans, useTranslation } from 'react-i18next' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' +import { useQuery } from 'react-query' import { loadAccountState } from './actions' import { Loader } from '../../shared/components/Loader' import './styles.scss' @@ -13,7 +14,6 @@ import InfoIcon from '../../shared/images/info.svg' import { useLanguage } from '../../shared/hooks' import Currency from '../../shared/components/Currency' import DomainLink from '../../shared/components/DomainLink' -import { useQuery } from 'react-query' const CURRENCY_OPTIONS = { style: 'currency', diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index 0236e36bc..143f28fbd 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -1,5 +1,6 @@ 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' @@ -8,7 +9,6 @@ import { Tabs } from '../shared/components/Tabs' import { AccountAssetTab } from './AccountAssetTab/AccountAssetTab' import { buildPath, useRouteParams } from '../shared/routing' import { ACCOUNT_ROUTE } from '../App/routes' -import { useQuery } from 'react-query' export const Accounts = () => { const { trackScreenLoaded } = useAnalytics() diff --git a/src/containers/CustomNetworkHome/index.tsx b/src/containers/CustomNetworkHome/index.tsx index 075525da3..769b5b89b 100644 --- a/src/containers/CustomNetworkHome/index.tsx +++ b/src/containers/CustomNetworkHome/index.tsx @@ -1,13 +1,13 @@ 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' import './index.scss' import { useCustomNetworks } from '../shared/hooks' import { Header } from '../Header' -import { useQuery } from 'react-query' const SidechainHome = () => { const { track, trackScreenLoaded } = useAnalytics() diff --git a/src/containers/Header/Search.tsx b/src/containers/Header/Search.tsx index a75246f83..37f15fe18 100644 --- a/src/containers/Header/Search.tsx +++ b/src/containers/Header/Search.tsx @@ -7,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' @@ -35,7 +36,6 @@ import { MPT_ROUTE, } from '../App/routes' import TokenSearchResults from '../shared/components/TokenSearchResults/TokenSearchResults' -import { useQuery } from 'react-query' const determineHashType = async (id: string, rippledContext: XrplClient) => { try { diff --git a/src/containers/Token/DEXPairs/index.tsx b/src/containers/Token/DEXPairs/index.tsx index b7b79d738..66b31f2f2 100644 --- a/src/containers/Token/DEXPairs/index.tsx +++ b/src/containers/Token/DEXPairs/index.tsx @@ -2,6 +2,7 @@ import { useState, useRef, useContext } from 'react' import { useTranslation } from 'react-i18next' import axios from 'axios' import './styles.scss' +import { useQuery } from 'react-query' import { useAnalytics } from '../../shared/analytics' import Log from '../../shared/log' import { Loader } from '../../shared/components/Loader' @@ -16,7 +17,6 @@ import Currency from '../../shared/components/Currency' import { Pair } from './types' import { TOKEN_ROUTE } from '../../App/routes' import { RouteLink } from '../../shared/routing' -import { useQuery } from 'react-query' // Hard Coded Pairs that we always check for const pairsHardCoded = [ diff --git a/src/containers/shared/NetworkContext.tsx b/src/containers/shared/NetworkContext.tsx index 709f59166..4ed2b9e6e 100644 --- a/src/containers/shared/NetworkContext.tsx +++ b/src/containers/shared/NetworkContext.tsx @@ -40,10 +40,9 @@ export const NetworkProvider = ({ async () => { if (initialNetworkName == null && rippledUrl) { try { - const resp = await axios.get( + const { data } = await axios.get( `${process.env.VITE_DATA_URL}/get_network/${rippledUrl}`, ) - const data = resp.data setNetworkName( data.result && data.result === 'error' ? null : data.network, ) diff --git a/src/containers/shared/components/Dropdown/Dropdown.tsx b/src/containers/shared/components/Dropdown/Dropdown.tsx index 86ce10bb0..fa76ab86a 100644 --- a/src/containers/shared/components/Dropdown/Dropdown.tsx +++ b/src/containers/shared/components/Dropdown/Dropdown.tsx @@ -44,11 +44,9 @@ export const Dropdown = ({ document.removeEventListener('click', globalClickListener) }, []) - useQuery(['dropdown-cleanup', globalClickListener], () => { - return () => { - // remove listener when cleaning up component - document.removeEventListener('click', globalClickListener) - } + useQuery(['dropdown-cleanup', globalClickListener], () => () => { + // remove listener when cleaning up component + document.removeEventListener('click', globalClickListener) }) const toggleExpand = () => { From 10ebd6bd80dff41147194edde28b592113090b28 Mon Sep 17 00:00:00 2001 From: Deeptanshu-sankhwar Date: Fri, 11 Apr 2025 20:34:56 -0600 Subject: [PATCH 4/4] sync with staging --- src/containers/Token/DEXPairs/index.tsx | 200 ------------------------ 1 file changed, 200 deletions(-) delete mode 100644 src/containers/Token/DEXPairs/index.tsx diff --git a/src/containers/Token/DEXPairs/index.tsx b/src/containers/Token/DEXPairs/index.tsx deleted file mode 100644 index 66b31f2f2..000000000 --- a/src/containers/Token/DEXPairs/index.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import { useState, useRef, useContext } from 'react' -import { useTranslation } from 'react-i18next' -import axios from 'axios' -import './styles.scss' -import { useQuery } from 'react-query' -import { useAnalytics } from '../../shared/analytics' -import Log from '../../shared/log' -import { Loader } from '../../shared/components/Loader' -import { - formatLargeNumber, - getLocalizedCurrencySymbol, -} from '../../shared/utils' -import { getOffers } from '../../../rippled' -import { PairStats } from './PairStats' -import SocketContext from '../../shared/SocketContext' -import Currency from '../../shared/components/Currency' -import { Pair } from './types' -import { TOKEN_ROUTE } from '../../App/routes' -import { RouteLink } from '../../shared/routing' - -// Hard Coded Pairs that we always check for -const pairsHardCoded = [ - { currency: 'XRP' }, - { currency: 'USD', issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' }, // Bitstamp USD - { currency: 'BTC', issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' }, // Bitstamp BTC -] - -export interface DexPairsProps { - accountId: string - currency: string -} - -export const DEXPairs = ({ accountId, currency }: DexPairsProps) => { - const { trackException } = useAnalytics() - const [pairs, setPairs] = useState([]) - const [isLoading, setIsLoading] = useState(true) - const isMountedRef = useRef(false) - const [isError, setIsError] = useState(false) - const { t } = useTranslation() - const rippledSocket = useContext(SocketContext) - - useQuery( - ['dex-pairs', accountId, currency], - async () => { - isMountedRef.current = true - const promises: Promise[] = [] - - try { - const tokenRes = await axios.get(`/api/v1/token/top`) - let tokenList = tokenRes.data?.tokens - if (tokenList) { - tokenList = tokenList.map((element) => ({ - currency: element.currency, - issuer: element.issuer, - })) - tokenList = tokenList.slice(0, 20) - } else { - tokenList = [] - } - - tokenList = tokenList.concat(pairsHardCoded) - - for (const token of tokenList) { - if ( - !( - token.currency.toUpperCase() === currency.toUpperCase() && - token.issuer === accountId - ) - ) { - promises.push( - getOffers( - currency, - accountId, - token.currency, - token.issuer, - rippledSocket, - ) - .then((data) => { - if (data.offers && data.offers.length > 0) { - let { - averageExchangeRate, - lowestExchangeRate, - highestExchangeRate, - } = data - if (token.currency === 'XRP') { - averageExchangeRate /= 1000000 - lowestExchangeRate /= 1000000 - highestExchangeRate /= 1000000 - } - - const low = formatLargeNumber(lowestExchangeRate, 6) - const high = formatLargeNumber(highestExchangeRate, 6) - const average = formatLargeNumber(averageExchangeRate, 6) - - if (isMountedRef.current) { - setPairs((previousPairs) => - previousPairs.concat([ - { - token: token.currency, - issuer: token.issuer, - low, - high, - average, - }, - ]), - ) - } - } - }) - .catch((err) => { - Log.error(err) - trackException( - `Error getting offers endpoint ${currency}.${accountId}/${token}`, - ) - }), - ) - } - } - - await Promise.allSettled(promises) - if (isMountedRef.current) { - setIsLoading(false) - } - } catch (err) { - if (isMountedRef.current) { - setIsError(true) - setIsLoading(false) - } - Log.error(err) - trackException(`Error getting top tokens from /api/v1/tokens/top`) - } - - return () => { - isMountedRef.current = false - } - }, - { - onSettled: () => { - if (isMountedRef.current) { - setIsLoading(false) - } - }, - }, - ) - - function renderNoPairs() { - return
{t('no_pairs_message')}
- } - const renderRow = (pair) => ( - - - /{' '} - {pair.average.num} {pair.average.unit} - - - {pair.issuer !== undefined ? ( - - {pair.issuer} - - ) : ( - getLocalizedCurrencySymbol('en-US', 'XRP') - )} - - - - - - ) - - return ( -
-

{t('top_trading_pairs')}

-
- - - - - - - - - - {pairs.map(renderRow)} - {isLoading && ( - - - - )} - -
{t('pair')}{t('issuer')}{t('offer_range')}
- -
- {(isError || (pairs.length === 0 && !isLoading)) && renderNoPairs()} -
-
- ) -}