Commit 3cc0a41e authored by Vignesh Mohankumar's avatar Vignesh Mohankumar Committed by GitHub

refactor: split ChainId useEffects (#4348)

* split out useOnSelectChain, useSyncChainQuery

* use chain query sync

* lint

* rename

* move variables
parent 4abdca9f
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { getConnection } from 'connection/utils'
import { getChainInfo } from 'constants/chainInfo' import { getChainInfo } from 'constants/chainInfo'
import { CHAIN_IDS_TO_NAMES, SupportedChainId } from 'constants/chains' import { SupportedChainId } from 'constants/chains'
import useParsedQueryString from 'hooks/useParsedQueryString' import useSelectChain from 'hooks/useSelectChain'
import usePrevious from 'hooks/usePrevious'
import { darken } from 'polished' import { darken } from 'polished'
import { ParsedQs } from 'qs' import { useRef } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { AlertTriangle, ArrowDownCircle, ChevronDown } from 'react-feather' import { AlertTriangle, ArrowDownCircle, ChevronDown } from 'react-feather'
import { useLocation, useNavigate } from 'react-router-dom'
import { useCloseModal, useModalIsOpen, useOpenModal, useToggleModal } from 'state/application/hooks' import { useCloseModal, useModalIsOpen, useOpenModal, useToggleModal } from 'state/application/hooks'
import { addPopup, ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
import { updateConnectionError } from 'state/connection/reducer'
import { useAppDispatch } from 'state/hooks'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme' import { ExternalLink, MEDIA_WIDTHS } from 'theme'
import { replaceURLParam } from 'utils/routes' import { isChainAllowed } from 'utils/switchChain'
import { isChainAllowed, switchChain } from 'utils/switchChain'
import { isMobile } from 'utils/userAgent' import { isMobile } from 'utils/userAgent'
const ActiveRowLinkList = styled.div` const ActiveRowLinkList = styled.div`
...@@ -278,24 +271,6 @@ function Row({ ...@@ -278,24 +271,6 @@ function Row({
return rowContent return rowContent
} }
const getParsedChainId = (parsedQs?: ParsedQs) => {
const chain = parsedQs?.chain
if (!chain || typeof chain !== 'string') return
return getChainIdFromName(chain)
}
const getChainIdFromName = (name: string) => {
const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([_, n]) => n === name)
const chainId = entry?.[0]
return chainId ? parseInt(chainId) : undefined
}
const getChainNameFromId = (id: string | number) => {
// casting here may not be right but fine to return undefined if it's not a supported chain ID
return CHAIN_IDS_TO_NAMES[id as SupportedChainId] || ''
}
const NETWORK_SELECTOR_CHAINS = [ const NETWORK_SELECTOR_CHAINS = [
SupportedChainId.MAINNET, SupportedChainId.MAINNET,
SupportedChainId.POLYGON, SupportedChainId.POLYGON,
...@@ -305,24 +280,7 @@ const NETWORK_SELECTOR_CHAINS = [ ...@@ -305,24 +280,7 @@ const NETWORK_SELECTOR_CHAINS = [
] ]
export default function NetworkSelector() { export default function NetworkSelector() {
const dispatch = useAppDispatch() const { chainId, provider, connector } = useWeb3React()
const { chainId, provider, connector, isActive } = useWeb3React()
const [previousChainId, setPreviousChainId] = useState<number | undefined>(undefined)
// Can't use `usePrevious` because `chainId` can be undefined while activating.
useEffect(() => {
if (chainId && chainId !== previousChainId) {
setPreviousChainId(chainId)
}
}, [chainId, previousChainId])
const parsedQs = useParsedQueryString()
const urlChainId = getParsedChainId(parsedQs)
const previousUrlChainId = usePrevious(urlChainId)
const navigate = useNavigate()
const { search } = useLocation()
const node = useRef<HTMLDivElement>(null) const node = useRef<HTMLDivElement>(null)
const isOpen = useModalIsOpen(ApplicationModal.NETWORK_SELECTOR) const isOpen = useModalIsOpen(ApplicationModal.NETWORK_SELECTOR)
...@@ -332,62 +290,7 @@ export default function NetworkSelector() { ...@@ -332,62 +290,7 @@ export default function NetworkSelector() {
const info = getChainInfo(chainId) const info = getChainInfo(chainId)
const replaceURLChainParam = useCallback(() => { const selectChain = useSelectChain()
if (chainId) {
navigate({ search: replaceURLParam(search, 'chain', getChainNameFromId(chainId)) }, { replace: true })
}
}, [chainId, search, navigate])
const onSelectChain = useCallback(
async (targetChain: SupportedChainId, skipClose?: boolean) => {
if (!connector) return
const connectionType = getConnection(connector).type
try {
dispatch(updateConnectionError({ connectionType, error: undefined }))
await switchChain(connector, targetChain)
} catch (error) {
console.error('Failed to switch networks', error)
dispatch(updateConnectionError({ connectionType, error: error.message }))
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
// If we activate a chain and it fails, reset the query param to the current chainId
replaceURLChainParam()
}
if (!skipClose) {
closeModal()
}
},
[connector, closeModal, dispatch, replaceURLChainParam]
)
// If there is no chain query param, set it to the current chain
useEffect(() => {
const chainQueryUnpopulated = !urlChainId
if (chainQueryUnpopulated && chainId) {
replaceURLChainParam()
}
}, [chainId, urlChainId, replaceURLChainParam])
// If the chain changed but the query param is stale, update to the current chain
useEffect(() => {
const chainChanged = chainId !== previousChainId
const chainQueryStale = urlChainId !== chainId
if (chainChanged && chainQueryStale) {
replaceURLChainParam()
}
}, [chainId, previousChainId, replaceURLChainParam, urlChainId])
// If the query param changed, and the chain didn't change, then activate the new chain
useEffect(() => {
const chainQueryManuallyUpdated = urlChainId && urlChainId !== previousUrlChainId
if (chainQueryManuallyUpdated && isActive) {
onSelectChain(urlChainId, true)
}
}, [onSelectChain, urlChainId, previousUrlChainId, isActive])
if (!chainId || !provider) { if (!chainId || !provider) {
return null return null
...@@ -425,7 +328,14 @@ export default function NetworkSelector() { ...@@ -425,7 +328,14 @@ export default function NetworkSelector() {
</FlyoutHeader> </FlyoutHeader>
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) => {NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) =>
isChainAllowed(connector, chainId) ? ( isChainAllowed(connector, chainId) ? (
<Row onSelectChain={onSelectChain} targetChain={chainId} key={chainId} /> <Row
onSelectChain={(targetChainId: SupportedChainId) => {
selectChain(targetChainId)
closeModal()
}}
targetChain={chainId}
key={chainId}
/>
) : null ) : null
)} )}
</FlyoutMenuContents> </FlyoutMenuContents>
......
import { useWeb3React } from '@web3-react/core'
import { getConnection } from 'connection/utils'
import { SupportedChainId } from 'constants/chains'
import { useCallback } from 'react'
import { addPopup } from 'state/application/reducer'
import { updateConnectionError } from 'state/connection/reducer'
import { useAppDispatch } from 'state/hooks'
import { switchChain } from 'utils/switchChain'
export default function useSelectChain() {
const dispatch = useAppDispatch()
const { connector } = useWeb3React()
return useCallback(
async (targetChain: SupportedChainId) => {
if (!connector) return
const connectionType = getConnection(connector).type
try {
dispatch(updateConnectionError({ connectionType, error: undefined }))
await switchChain(connector, targetChain)
} catch (error) {
console.error('Failed to switch networks', error)
dispatch(updateConnectionError({ connectionType, error: error.message }))
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
}
},
[connector, dispatch]
)
}
import { useWeb3React } from '@web3-react/core'
import { CHAIN_IDS_TO_NAMES, SupportedChainId } from 'constants/chains'
import { ParsedQs } from 'qs'
import { useCallback, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { replaceURLParam } from 'utils/routes'
import useParsedQueryString from './useParsedQueryString'
import usePrevious from './usePrevious'
import useSelectChain from './useSelectChain'
function getChainIdFromName(name: string) {
const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([_, n]) => n === name)
const chainId = entry?.[0]
return chainId ? parseInt(chainId) : undefined
}
function getParsedChainId(parsedQs?: ParsedQs) {
const chain = parsedQs?.chain
if (!chain || typeof chain !== 'string') return
return getChainIdFromName(chain)
}
function getChainNameFromId(id: string | number) {
// casting here may not be right but fine to return undefined if it's not a supported chain ID
return CHAIN_IDS_TO_NAMES[id as SupportedChainId] || ''
}
export default function useSyncChainQuery() {
const { chainId, isActive } = useWeb3React()
const navigate = useNavigate()
const { search } = useLocation()
const parsedQs = useParsedQueryString()
const urlChainId = getParsedChainId(parsedQs)
const previousUrlChainId = usePrevious(urlChainId)
const selectChain = useSelectChain()
// Can't use `usePrevious` because `chainId` can be undefined while activating.
const [previousChainId, setPreviousChainId] = useState<number | undefined>(undefined)
useEffect(() => {
if (chainId && chainId !== previousChainId) {
setPreviousChainId(chainId)
}
}, [chainId, previousChainId])
const replaceURLChainParam = useCallback(() => {
if (chainId) {
navigate({ search: replaceURLParam(search, 'chain', getChainNameFromId(chainId)) }, { replace: true })
}
}, [chainId, search, navigate])
const chainQueryUnpopulated = !urlChainId && chainId
const chainChanged = chainId !== previousChainId
const chainQueryStale = urlChainId !== chainId
const chainQueryManuallyUpdated = urlChainId && urlChainId !== previousUrlChainId && isActive
return useEffect(() => {
if (chainQueryUnpopulated) {
// If there is no chain query param, set it to the current chain
replaceURLChainParam()
} else if (chainChanged && chainQueryStale) {
// If the chain changed but the query param is stale, update to the current chain
replaceURLChainParam()
} else if (chainQueryManuallyUpdated) {
// If the query param changed, and the chain didn't change, then activate the new chain
selectChain(urlChainId)
}
}, [
chainQueryUnpopulated,
chainChanged,
chainQueryStale,
chainQueryManuallyUpdated,
urlChainId,
selectChain,
replaceURLChainParam,
])
}
...@@ -6,6 +6,7 @@ import TopLevelModals from 'components/TopLevelModals' ...@@ -6,6 +6,7 @@ import TopLevelModals from 'components/TopLevelModals'
import { useFeatureFlagsIsLoaded } from 'featureFlags' import { useFeatureFlagsIsLoaded } from 'featureFlags'
import { ExploreVariant, useExploreFlag } from 'featureFlags/flags/explore' import { ExploreVariant, useExploreFlag } from 'featureFlags/flags/explore'
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader' import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
import useSyncChainQuery from 'hooks/useSyncChainQuery'
import { lazy, Suspense, useEffect } from 'react' import { lazy, Suspense, useEffect } from 'react'
import { Navigate, Route, Routes, useLocation } from 'react-router-dom' import { Navigate, Route, Routes, useLocation } from 'react-router-dom'
import { useIsDarkMode } from 'state/user/hooks' import { useIsDarkMode } from 'state/user/hooks'
...@@ -114,6 +115,8 @@ export default function App() { ...@@ -114,6 +115,8 @@ export default function App() {
useAnalyticsReporter() useAnalyticsReporter()
initializeAnalytics() initializeAnalytics()
useSyncChainQuery()
useEffect(() => { useEffect(() => {
window.scrollTo(0, 0) window.scrollTo(0, 0)
}, [pathname]) }, [pathname])
......
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