Commit f6ceecbc authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

fix: update hook deps to improve ref equality checks (#3707)

* fix: prevent unnecessary TokenImg renders

* fix: prevent unnecessary trade renders
parent a0348b45
...@@ -13,6 +13,7 @@ import useUSDCPrice, { useUSDCValue } from './useUSDCPrice' ...@@ -13,6 +13,7 @@ import useUSDCPrice, { useUSDCValue } from './useUSDCPrice'
const V3_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50% const V3_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
const ONE_TENTHS_PERCENT = new Percent(10, 10_000) // .10% 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 * Return a guess of the gas cost used in computing slippage tolerance for a given trade
...@@ -44,7 +45,7 @@ export default function useAutoSlippageTolerance( ...@@ -44,7 +45,7 @@ export default function useAutoSlippageTolerance(
const nativeCurrencyPrice = useUSDCPrice((trade && nativeCurrency) ?? undefined) const nativeCurrencyPrice = useUSDCPrice((trade && nativeCurrency) ?? undefined)
return useMemo(() => { return useMemo(() => {
if (!trade || onL2) return ONE_TENTHS_PERCENT if (!trade || onL2) return DEFAULT_AUTO_SLIPPAGE
const nativeGasCost = const nativeGasCost =
nativeGasPrice && typeof gasEstimate === 'number' nativeGasPrice && typeof gasEstimate === 'number'
......
import { Currency, CurrencyAmount, Price, Token, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Price, Token, TradeType } from '@uniswap/sdk-core'
import useActiveWeb3React from 'hooks/useActiveWeb3React' import useActiveWeb3React from 'hooks/useActiveWeb3React'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react' import { useMemo, useRef } from 'react'
import { SupportedChainId } from '../constants/chains' import { SupportedChainId } from '../constants/chains'
import { DAI_OPTIMISM, USDC_ARBITRUM, USDC_MAINNET, USDC_POLYGON } from '../constants/tokens' 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 ...@@ -32,8 +32,7 @@ export default function useUSDCPrice(currency?: Currency): Price<Currency, Token
maxHops: 2, maxHops: 2,
}) })
const v3USDCTrade = useClientSideV3Trade(TradeType.EXACT_OUTPUT, amountOut, currency) const v3USDCTrade = useClientSideV3Trade(TradeType.EXACT_OUTPUT, amountOut, currency)
const price = useMemo(() => {
return useMemo(() => {
if (!currency || !stablecoin) { if (!currency || !stablecoin) {
return undefined return undefined
} }
...@@ -54,6 +53,12 @@ export default function useUSDCPrice(currency?: Currency): Price<Currency, Token ...@@ -54,6 +53,12 @@ export default function useUSDCPrice(currency?: Currency): Price<Currency, Token
return undefined return undefined
}, [currency, stablecoin, v2USDCTrade, v3USDCTrade.trade]) }, [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) { export function useUSDCValue(currencyAmount: CurrencyAmount<Currency> | undefined | null) {
......
...@@ -26,16 +26,17 @@ function TokenImg({ token, ...rest }: TokenImgProps) { ...@@ -26,16 +26,17 @@ function TokenImg({ token, ...rest }: TokenImgProps) {
setAttempt((attempt) => ++attempt) setAttempt((attempt) => ++attempt)
}, []) }, [])
return useMemo(() => { const src = useMemo(() => {
// Trigger a re-render when an error occurs. // Trigger a re-render when an error occurs.
void attempt void attempt
const src = srcs.find((src) => !badSrcs.has(src)) return srcs.find((src) => !badSrcs.has(src))
if (!src) return <MissingToken color="secondary" {...rest} /> }, [attempt, srcs])
const alt = tokenInfo.name || tokenInfo.symbol if (!src) return <MissingToken color="secondary" {...rest} />
return <img src={src} alt={alt} key={alt} onError={onError} {...rest} />
}, [attempt, onError, rest, srcs, tokenInfo.name, tokenInfo.symbol]) const alt = tokenInfo.name || tokenInfo.symbol
return <img src={src} alt={alt} key={alt} onError={onError} {...rest} />
} }
export default styled(TokenImg)<{ size?: number }>` export default styled(TokenImg)<{ size?: number }>`
......
...@@ -6,6 +6,8 @@ import { InterfaceTrade, TradeState } from 'state/routing/types' ...@@ -6,6 +6,8 @@ import { InterfaceTrade, TradeState } from 'state/routing/types'
import useClientSideSmartOrderRouterTrade from '../routing/useClientSideSmartOrderRouterTrade' import useClientSideSmartOrderRouterTrade from '../routing/useClientSideSmartOrderRouterTrade'
export const INVALID_TRADE = { state: TradeState.INVALID, trade: undefined }
/** /**
* Returns the best v2+v3 trade for a desired swap. * Returns the best v2+v3 trade for a desired swap.
* @param tradeType whether the swap is an exact in/out * @param tradeType whether the swap is an exact in/out
...@@ -39,6 +41,7 @@ export function useBestTrade( ...@@ -39,6 +41,7 @@ export function useBestTrade(
return useMemo(() => { return useMemo(() => {
const { state, trade } = tradeObject const { state, trade } = tradeObject
// If the trade is in a settled state, return it. // 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 if ((state !== TradeState.LOADING && state !== TradeState.SYNCING) || trade) return tradeObject
const [currencyIn, currencyOut] = const [currencyIn, currencyOut] =
......
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { atom } from 'jotai' import { atom } from 'jotai'
import { useAtomValue, useUpdateAtom } from 'jotai/utils' import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React' import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance' 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 useUSDCPriceImpact, { PriceImpact } from 'lib/hooks/useUSDCPriceImpact'
import { Field, swapAtom } from 'lib/state/swap' import { Field, swapAtom } from 'lib/state/swap'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useEffect, useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { InterfaceTrade, TradeState } from 'state/routing/types' import { InterfaceTrade, TradeState } from 'state/routing/types'
import { useBestTrade } from './useBestTrade' import { INVALID_TRADE, useBestTrade } from './useBestTrade'
import useWrapCallback, { WrapType } from './useWrapCallback' import useWrapCallback, { WrapType } from './useWrapCallback'
interface SwapField { interface SwapField {
...@@ -84,8 +84,8 @@ function useComputeSwapInfo(): SwapInfo { ...@@ -84,8 +84,8 @@ function useComputeSwapInfo(): SwapInfo {
const swapInfoAtom = atom<SwapInfo>({ const swapInfoAtom = atom<SwapInfo>({
[Field.INPUT]: {}, [Field.INPUT]: {},
[Field.OUTPUT]: {}, [Field.OUTPUT]: {},
trade: { state: TradeState.INVALID }, trade: INVALID_TRADE,
slippage: { auto: true, allowed: new Percent(0) }, slippage: DEFAULT_SLIPPAGE,
}) })
export function SwapInfoUpdater() { export function SwapInfoUpdater() {
...@@ -100,17 +100,19 @@ export default function useSwapInfo(): SwapInfo { ...@@ -100,17 +100,19 @@ export default function useSwapInfo(): SwapInfo {
const swapInfo = useAtomValue(swapInfoAtom) const swapInfo = useAtomValue(swapInfoAtom)
const { [Field.INPUT]: currencyIn, [Field.OUTPUT]: currencyOut } = useAtomValue(swapAtom) const { [Field.INPUT]: currencyIn, [Field.OUTPUT]: currencyOut } = useAtomValue(swapAtom)
const tradeState = useMemo(() => { const trade = useMemo(() => {
const { trade } = swapInfo const trade = swapInfo.trade
if (trade.state === TradeState.VALID && trade.trade) { if (trade.state === TradeState.VALID && trade.trade) {
const isTradeStale = if (
(currencyIn && !trade.trade.inputAmount.currency.equals(currencyIn)) || (currencyIn && !trade.trade.inputAmount.currency.equals(currencyIn)) ||
(currencyOut && !trade.trade.outputAmount.currency.equals(currencyOut)) (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 return trade
}, [currencyIn, currencyOut, swapInfo]) }, [currencyIn, currencyOut, swapInfo.trade])
const { account } = useActiveWeb3React() const { account } = useActiveWeb3React()
const [balanceIn, balanceOut] = useCurrencyBalances( const [balanceIn, balanceOut] = useCurrencyBalances(
...@@ -121,13 +123,16 @@ export default function useSwapInfo(): SwapInfo { ...@@ -121,13 +123,16 @@ export default function useSwapInfo(): SwapInfo {
// swapInfo will lag behind swapAtom by a frame, because its update is triggered by swapAtom // 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. // 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. // 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( return useMemo(
() => ({ () => ({ ...swapInfo, trade, [Field.INPUT]: input, [Field.OUTPUT]: output }),
...swapInfo, [input, output, swapInfo, trade]
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]
) )
} }
import { Currency, Percent, TradeType } from '@uniswap/sdk-core' 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 { useAtomValue } from 'jotai/utils'
import { autoSlippageAtom, maxSlippageAtom } from 'lib/state/settings' import { autoSlippageAtom, maxSlippageAtom } from 'lib/state/settings'
import { useMemo } from 'react' import { useMemo } from 'react'
...@@ -17,6 +17,8 @@ export interface Slippage { ...@@ -17,6 +17,8 @@ export interface Slippage {
warning?: 'warning' | 'error' warning?: 'warning' | 'error'
} }
export const DEFAULT_SLIPPAGE = { auto: true, allowed: DEFAULT_AUTO_SLIPPAGE }
/** Returns the allowed slippage, and whether it is auto-slippage. */ /** Returns the allowed slippage, and whether it is auto-slippage. */
export default function useSlippage(trade: InterfaceTrade<Currency, Currency, TradeType> | undefined): Slippage { export default function useSlippage(trade: InterfaceTrade<Currency, Currency, TradeType> | undefined): Slippage {
const shouldUseAutoSlippage = useAtomValue(autoSlippageAtom) const shouldUseAutoSlippage = useAtomValue(autoSlippageAtom)
...@@ -27,6 +29,9 @@ export default function useSlippage(trade: InterfaceTrade<Currency, Currency, Tr ...@@ -27,6 +29,9 @@ export default function useSlippage(trade: InterfaceTrade<Currency, Currency, Tr
const auto = shouldUseAutoSlippage || !maxSlippage const auto = shouldUseAutoSlippage || !maxSlippage
const allowed = shouldUseAutoSlippage ? autoSlippage : maxSlippage ?? autoSlippage const allowed = shouldUseAutoSlippage ? autoSlippage : maxSlippage ?? autoSlippage
const warning = auto ? undefined : getSlippageWarning(allowed) const warning = auto ? undefined : getSlippageWarning(allowed)
if (auto && allowed === DEFAULT_AUTO_SLIPPAGE) {
return DEFAULT_SLIPPAGE
}
return { auto, allowed, warning } return { auto, allowed, warning }
}, [autoSlippage, maxSlippage, shouldUseAutoSlippage]) }, [autoSlippage, maxSlippage, shouldUseAutoSlippage])
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment