fix: update hook deps to improve ref equality checks (#3707)
* fix: prevent unnecessary TokenImg renders * fix: prevent unnecessary trade renders
This commit is contained in:
parent
a0348b45be
commit
f6ceecbc5e
@ -13,6 +13,7 @@ import useUSDCPrice, { useUSDCValue } from './useUSDCPrice'
|
||||
|
||||
const V3_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
|
||||
const ONE_TENTHS_PERCENT = new Percent(10, 10_000) // .10%
|
||||
export const DEFAULT_AUTO_SLIPPAGE = ONE_TENTHS_PERCENT
|
||||
|
||||
/**
|
||||
* Return a guess of the gas cost used in computing slippage tolerance for a given trade
|
||||
@ -44,7 +45,7 @@ export default function useAutoSlippageTolerance(
|
||||
const nativeCurrencyPrice = useUSDCPrice((trade && nativeCurrency) ?? undefined)
|
||||
|
||||
return useMemo(() => {
|
||||
if (!trade || onL2) return ONE_TENTHS_PERCENT
|
||||
if (!trade || onL2) return DEFAULT_AUTO_SLIPPAGE
|
||||
|
||||
const nativeGasCost =
|
||||
nativeGasPrice && typeof gasEstimate === 'number'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Currency, CurrencyAmount, Price, Token, TradeType } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { useMemo } from 'react'
|
||||
import { useMemo, useRef } from 'react'
|
||||
|
||||
import { SupportedChainId } from '../constants/chains'
|
||||
import { DAI_OPTIMISM, USDC_ARBITRUM, USDC_MAINNET, USDC_POLYGON } from '../constants/tokens'
|
||||
@ -32,8 +32,7 @@ export default function useUSDCPrice(currency?: Currency): Price<Currency, Token
|
||||
maxHops: 2,
|
||||
})
|
||||
const v3USDCTrade = useClientSideV3Trade(TradeType.EXACT_OUTPUT, amountOut, currency)
|
||||
|
||||
return useMemo(() => {
|
||||
const price = useMemo(() => {
|
||||
if (!currency || !stablecoin) {
|
||||
return undefined
|
||||
}
|
||||
@ -54,6 +53,12 @@ export default function useUSDCPrice(currency?: Currency): Price<Currency, Token
|
||||
|
||||
return undefined
|
||||
}, [currency, stablecoin, v2USDCTrade, v3USDCTrade.trade])
|
||||
|
||||
const lastPrice = useRef(price)
|
||||
if (!price || !lastPrice.current || !price.equalTo(lastPrice.current)) {
|
||||
lastPrice.current = price
|
||||
}
|
||||
return lastPrice.current
|
||||
}
|
||||
|
||||
export function useUSDCValue(currencyAmount: CurrencyAmount<Currency> | undefined | null) {
|
||||
|
@ -26,16 +26,17 @@ function TokenImg({ token, ...rest }: TokenImgProps) {
|
||||
setAttempt((attempt) => ++attempt)
|
||||
}, [])
|
||||
|
||||
return useMemo(() => {
|
||||
const src = useMemo(() => {
|
||||
// Trigger a re-render when an error occurs.
|
||||
void attempt
|
||||
|
||||
const src = srcs.find((src) => !badSrcs.has(src))
|
||||
if (!src) return <MissingToken color="secondary" {...rest} />
|
||||
return srcs.find((src) => !badSrcs.has(src))
|
||||
}, [attempt, srcs])
|
||||
|
||||
const alt = tokenInfo.name || tokenInfo.symbol
|
||||
return <img src={src} alt={alt} key={alt} onError={onError} {...rest} />
|
||||
}, [attempt, onError, rest, srcs, tokenInfo.name, tokenInfo.symbol])
|
||||
if (!src) return <MissingToken color="secondary" {...rest} />
|
||||
|
||||
const alt = tokenInfo.name || tokenInfo.symbol
|
||||
return <img src={src} alt={alt} key={alt} onError={onError} {...rest} />
|
||||
}
|
||||
|
||||
export default styled(TokenImg)<{ size?: number }>`
|
||||
|
@ -6,6 +6,8 @@ import { InterfaceTrade, TradeState } from 'state/routing/types'
|
||||
|
||||
import useClientSideSmartOrderRouterTrade from '../routing/useClientSideSmartOrderRouterTrade'
|
||||
|
||||
export const INVALID_TRADE = { state: TradeState.INVALID, trade: undefined }
|
||||
|
||||
/**
|
||||
* Returns the best v2+v3 trade for a desired swap.
|
||||
* @param tradeType whether the swap is an exact in/out
|
||||
@ -39,6 +41,7 @@ export function useBestTrade(
|
||||
return useMemo(() => {
|
||||
const { state, trade } = tradeObject
|
||||
// If the trade is in a settled state, return it.
|
||||
if (state === TradeState.INVALID) return INVALID_TRADE
|
||||
if ((state !== TradeState.LOADING && state !== TradeState.SYNCING) || trade) return tradeObject
|
||||
|
||||
const [currencyIn, currencyOut] =
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
|
||||
import { atom } from 'jotai'
|
||||
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
|
||||
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
|
||||
import useSlippage, { Slippage } from 'lib/hooks/useSlippage'
|
||||
import useSlippage, { DEFAULT_SLIPPAGE, Slippage } from 'lib/hooks/useSlippage'
|
||||
import useUSDCPriceImpact, { PriceImpact } from 'lib/hooks/useUSDCPriceImpact'
|
||||
import { Field, swapAtom } from 'lib/state/swap'
|
||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { InterfaceTrade, TradeState } from 'state/routing/types'
|
||||
|
||||
import { useBestTrade } from './useBestTrade'
|
||||
import { INVALID_TRADE, useBestTrade } from './useBestTrade'
|
||||
import useWrapCallback, { WrapType } from './useWrapCallback'
|
||||
|
||||
interface SwapField {
|
||||
@ -84,8 +84,8 @@ function useComputeSwapInfo(): SwapInfo {
|
||||
const swapInfoAtom = atom<SwapInfo>({
|
||||
[Field.INPUT]: {},
|
||||
[Field.OUTPUT]: {},
|
||||
trade: { state: TradeState.INVALID },
|
||||
slippage: { auto: true, allowed: new Percent(0) },
|
||||
trade: INVALID_TRADE,
|
||||
slippage: DEFAULT_SLIPPAGE,
|
||||
})
|
||||
|
||||
export function SwapInfoUpdater() {
|
||||
@ -100,17 +100,19 @@ export default function useSwapInfo(): SwapInfo {
|
||||
const swapInfo = useAtomValue(swapInfoAtom)
|
||||
|
||||
const { [Field.INPUT]: currencyIn, [Field.OUTPUT]: currencyOut } = useAtomValue(swapAtom)
|
||||
const tradeState = useMemo(() => {
|
||||
const { trade } = swapInfo
|
||||
const trade = useMemo(() => {
|
||||
const trade = swapInfo.trade
|
||||
if (trade.state === TradeState.VALID && trade.trade) {
|
||||
const isTradeStale =
|
||||
if (
|
||||
(currencyIn && !trade.trade.inputAmount.currency.equals(currencyIn)) ||
|
||||
(currencyOut && !trade.trade.outputAmount.currency.equals(currencyOut))
|
||||
// swapInfo has not yet caught up to swapAtom.
|
||||
if (isTradeStale) return TradeState.LOADING
|
||||
) {
|
||||
// swapInfo has not yet caught up to swapAtom.
|
||||
return { ...trade, state: TradeState.LOADING }
|
||||
}
|
||||
}
|
||||
return trade.state
|
||||
}, [currencyIn, currencyOut, swapInfo])
|
||||
return trade
|
||||
}, [currencyIn, currencyOut, swapInfo.trade])
|
||||
|
||||
const { account } = useActiveWeb3React()
|
||||
const [balanceIn, balanceOut] = useCurrencyBalances(
|
||||
@ -121,13 +123,16 @@ export default function useSwapInfo(): SwapInfo {
|
||||
// swapInfo will lag behind swapAtom by a frame, because its update is triggered by swapAtom
|
||||
// so a swap must be marked as loading, with up-to-date currencies, during that update.
|
||||
// In other words, swapInfo is derived from swapAtom, so it must be used as the source of truth.
|
||||
const input = useMemo(
|
||||
() => ({ ...swapInfo[Field.INPUT], currency: currencyIn, balance: balanceIn }),
|
||||
[balanceIn, currencyIn, swapInfo]
|
||||
)
|
||||
const output = useMemo(
|
||||
() => ({ ...swapInfo[Field.OUTPUT], currency: currencyOut, balance: balanceOut }),
|
||||
[balanceOut, currencyOut, swapInfo]
|
||||
)
|
||||
return useMemo(
|
||||
() => ({
|
||||
...swapInfo,
|
||||
trade: { ...swapInfo.trade, state: tradeState },
|
||||
[Field.INPUT]: { ...swapInfo[Field.INPUT], currency: currencyIn, balance: balanceIn },
|
||||
[Field.OUTPUT]: { ...swapInfo[Field.OUTPUT], currency: currencyOut, balance: balanceOut },
|
||||
}),
|
||||
[balanceIn, balanceOut, currencyIn, currencyOut, swapInfo, tradeState]
|
||||
() => ({ ...swapInfo, trade, [Field.INPUT]: input, [Field.OUTPUT]: output }),
|
||||
[input, output, swapInfo, trade]
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
|
||||
import useAutoSlippageTolerance, { DEFAULT_AUTO_SLIPPAGE } from 'hooks/useAutoSlippageTolerance'
|
||||
import { useAtomValue } from 'jotai/utils'
|
||||
import { autoSlippageAtom, maxSlippageAtom } from 'lib/state/settings'
|
||||
import { useMemo } from 'react'
|
||||
@ -17,6 +17,8 @@ export interface Slippage {
|
||||
warning?: 'warning' | 'error'
|
||||
}
|
||||
|
||||
export const DEFAULT_SLIPPAGE = { auto: true, allowed: DEFAULT_AUTO_SLIPPAGE }
|
||||
|
||||
/** Returns the allowed slippage, and whether it is auto-slippage. */
|
||||
export default function useSlippage(trade: InterfaceTrade<Currency, Currency, TradeType> | undefined): Slippage {
|
||||
const shouldUseAutoSlippage = useAtomValue(autoSlippageAtom)
|
||||
@ -27,6 +29,9 @@ export default function useSlippage(trade: InterfaceTrade<Currency, Currency, Tr
|
||||
const auto = shouldUseAutoSlippage || !maxSlippage
|
||||
const allowed = shouldUseAutoSlippage ? autoSlippage : maxSlippage ?? autoSlippage
|
||||
const warning = auto ? undefined : getSlippageWarning(allowed)
|
||||
if (auto && allowed === DEFAULT_AUTO_SLIPPAGE) {
|
||||
return DEFAULT_SLIPPAGE
|
||||
}
|
||||
return { auto, allowed, warning }
|
||||
}, [autoSlippage, maxSlippage, shouldUseAutoSlippage])
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user