Commit 5afddb2e authored by Moody Salem's avatar Moody Salem

Reduce the spam of redux events

parent 3b2c21e2
import { useEffect } from 'react'
import { useWeb3React } from '../../hooks'
import { useEffect, useState } from 'react'
import { useDebounce, useWeb3React } from '../../hooks'
import { updateBlockNumber } from './actions'
import { useDispatch } from 'react-redux'
......@@ -7,17 +7,27 @@ export default function Updater() {
const { library, chainId } = useWeb3React()
const dispatch = useDispatch()
const [maxBlockNumber, setMaxBlockNumber] = useState<number | null>(null)
// because blocks arrive in bunches with longer polling periods, we just want
// to process the latest one.
const debouncedMaxBlockNumber = useDebounce<number | null>(maxBlockNumber, 100)
// update block number
useEffect(() => {
if (!library || !chainId) return
const blockListener = (blockNumber: number) => {
dispatch(updateBlockNumber({ chainId, blockNumber }))
setMaxBlockNumber(maxBlockNumber => {
if (typeof maxBlockNumber !== 'number') return blockNumber
return Math.max(maxBlockNumber, blockNumber)
})
}
setMaxBlockNumber(null)
library
.getBlockNumber()
.then(blockNumber => updateBlockNumber({ chainId, blockNumber }))
.then(blockNumber => dispatch(updateBlockNumber({ chainId, blockNumber })))
.catch(error => console.error(`Failed to get block number for chainId ${chainId}`, error))
library.on('block', blockListener)
......@@ -26,5 +36,10 @@ export default function Updater() {
}
}, [dispatch, chainId, library])
useEffect(() => {
if (!chainId || !debouncedMaxBlockNumber) return
dispatch(updateBlockNumber({ chainId, blockNumber: debouncedMaxBlockNumber }))
}, [chainId, debouncedMaxBlockNumber, dispatch])
return null
}
......@@ -18,7 +18,6 @@ export const addTransaction = createAction<{
approvalOfToken?: string
summary?: string
}>('addTransaction')
export const checkTransaction = createAction<{ chainId: number; hash: string; blockNumber: number }>('checkTransaction')
export const clearAllTransactions = createAction<{ chainId: number }>('clearAllTransactions')
export const finalizeTransaction = createAction<{
chainId: number
......
import { createReducer } from '@reduxjs/toolkit'
import {
addTransaction,
checkTransaction,
clearAllTransactions,
finalizeTransaction,
SerializableTransactionReceipt
} from './actions'
import { addTransaction, clearAllTransactions, finalizeTransaction, SerializableTransactionReceipt } from './actions'
const now = () => new Date().getTime()
export interface TransactionDetails {
hash: string
approvalOfToken?: string
blockNumberChecked?: number
summary?: string
receipt?: SerializableTransactionReceipt
addedTime: number
......@@ -44,13 +37,6 @@ export default createReducer(initialState, builder =>
if (!state[chainId]) return
state[chainId] = {}
})
.addCase(checkTransaction, (state, { payload: { chainId, blockNumber, hash } }) => {
if (!state[chainId]?.[hash]) {
throw Error('Attempted to check non-existent transaction.')
}
state[chainId][hash].blockNumberChecked = Math.max(blockNumber ?? 0, state[chainId][hash].blockNumberChecked ?? 0)
})
.addCase(finalizeTransaction, (state, { payload: { hash, chainId, receipt } }) => {
if (!state[chainId]?.[hash]) {
throw Error('Attempted to finalize non-existent transaction.')
......
......@@ -3,7 +3,7 @@ import { useDispatch, useSelector } from 'react-redux'
import { useWeb3React } from '../../hooks'
import { useAddPopup, useBlockNumber } from '../application/hooks'
import { AppDispatch, AppState } from '../index'
import { checkTransaction, finalizeTransaction } from './actions'
import { finalizeTransaction } from './actions'
export default function Updater() {
const { chainId, library } = useWeb3React()
......@@ -19,24 +19,15 @@ export default function Updater() {
const addPopup = useAddPopup()
useEffect(() => {
if (!chainId) return
if (!library) return
if (!lastBlockNumber) return
if (!chainId || !library || !lastBlockNumber) return
Object.keys(allTransactions)
.filter(hash => !allTransactions[hash].receipt)
.filter(hash => {
const lastChecked = allTransactions[hash].blockNumberChecked
return !lastChecked || lastChecked < lastBlockNumber
})
.forEach(hash => {
library
.getTransactionReceipt(hash)
.then(receipt => {
if (!receipt) {
dispatch(checkTransaction({ chainId, hash, blockNumber: lastBlockNumber }))
} else {
if (receipt) {
dispatch(
finalizeTransaction({
chainId,
......
......@@ -33,15 +33,19 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): { [
[uncheckedAddresses]
)
// used so that we do a deep comparison in `useEffect`
const serializedAddresses = JSON.stringify(addresses)
// add the listeners on mount, remove them on dismount
useEffect(() => {
const addresses = JSON.parse(serializedAddresses)
if (addresses.length === 0) return
dispatch(startListeningForBalance({ addresses }))
return () => {
dispatch(stopListeningForBalance({ addresses }))
}
}, [addresses, dispatch])
}, [serializedAddresses, dispatch])
const rawBalanceMap = useSelector<AppState, AppState['wallet']['balances']>(({ wallet: { balances } }) => balances)
......@@ -72,19 +76,29 @@ export function useTokenBalances(
() => tokens?.filter((t?: Token): t is Token => isAddress(t?.address) !== false) ?? [],
[tokens]
)
const tokenAddresses: string[] = useMemo(() => validTokens.map(t => t.address).sort(), [validTokens])
// used so that we do a deep comparison in `useEffect`
const serializedCombos: string = useMemo(() => {
return JSON.stringify(
!address || validTokens.length === 0
? []
: validTokens
.map(t => t.address)
.sort()
.map(tokenAddress => ({ address, tokenAddress }))
)
}, [address, validTokens])
// keep the listeners up to date
useEffect(() => {
if (!address) return
if (tokenAddresses.length === 0) return
const combos: TokenBalanceListenerKey[] = JSON.parse(serializedCombos)
if (combos.length === 0) return
const combos: TokenBalanceListenerKey[] = tokenAddresses.map(tokenAddress => ({ address, tokenAddress }))
dispatch(startListeningForTokenBalances(combos))
return () => {
dispatch(stopListeningForTokenBalances(combos))
}
}, [address, tokenAddresses, dispatch])
}, [address, serializedCombos, dispatch])
const rawBalanceMap = useSelector<AppState, AppState['wallet']['balances']>(({ wallet: { balances } }) => balances)
......
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