1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-29 15:50:28 +01:00
metamask-extension/ui/hooks/useTokenTracker.js
Matthew Epps 7a92e22111
Swaps optimizations (#12842)
* Trigger Build

* Trigger Build

* Move swaps index variables to redux

* all optimizations so far

* Add better equality checks for selectors in swaps index and build quote

* Clean up PR, remove extra code and logs

* Clean up lavamoat file

* Fixes for optimizations

* Update tests and test snapshots

* Remove unnecessary tests

* Remove unnecessary console log

* Trigger Build

* Trigger Build

* Add delay to account for remote call made by trezor keyring

Co-authored-by: Dan Miller <danjm.com@gmail.com>
2021-12-01 12:55:09 -03:30

123 lines
4.3 KiB
JavaScript

import { useState, useEffect, useRef, useCallback } from 'react';
import TokenTracker from '@metamask/eth-token-tracker';
import { shallowEqual, useSelector } from 'react-redux';
import { getCurrentChainId, getSelectedAddress } from '../selectors';
import { SECOND } from '../../shared/constants/time';
import { isEqualCaseInsensitive } from '../helpers/utils/util';
import { useEqualityCheck } from './useEqualityCheck';
export function useTokenTracker(
tokens,
includeFailedTokens = false,
hideZeroBalanceTokens = false,
) {
const chainId = useSelector(getCurrentChainId);
const userAddress = useSelector(getSelectedAddress, shallowEqual);
const [loading, setLoading] = useState(() => tokens?.length >= 0);
const [tokensWithBalances, setTokensWithBalances] = useState([]);
const [error, setError] = useState(null);
const tokenTracker = useRef(null);
const memoizedTokens = useEqualityCheck(tokens);
const updateBalances = useCallback(
(tokenWithBalances) => {
const matchingTokens = hideZeroBalanceTokens
? tokenWithBalances.filter((token) => Number(token.balance) > 0)
: tokenWithBalances;
// TODO: improve this pattern for adding this field when we improve support for
// EIP721 tokens.
const matchingTokensWithIsERC721Flag = matchingTokens.map((token) => {
const additionalTokenData = memoizedTokens.find((t) =>
isEqualCaseInsensitive(t.address, token.address),
);
return {
...token,
isERC721: additionalTokenData?.isERC721,
image: additionalTokenData?.image,
};
});
setTokensWithBalances(matchingTokensWithIsERC721Flag);
setLoading(false);
setError(null);
},
[hideZeroBalanceTokens, memoizedTokens],
);
const showError = useCallback((err) => {
setError(err);
setLoading(false);
}, []);
const teardownTracker = useCallback(() => {
if (tokenTracker.current) {
tokenTracker.current.stop();
tokenTracker.current.removeAllListeners('update');
tokenTracker.current.removeAllListeners('error');
tokenTracker.current = null;
}
}, []);
const buildTracker = useCallback(
(address, tokenList) => {
// clear out previous tracker, if it exists.
teardownTracker();
tokenTracker.current = new TokenTracker({
userAddress: address,
provider: global.ethereumProvider,
tokens: tokenList,
includeFailedTokens,
pollingInterval: SECOND * 8,
});
tokenTracker.current.on('update', updateBalances);
tokenTracker.current.on('error', showError);
tokenTracker.current.updateBalances();
},
[updateBalances, includeFailedTokens, showError, teardownTracker],
);
// Effect to remove the tracker when the component is removed from DOM
// Do not overload this effect with additional dependencies. teardownTracker
// is the only dependency here, which itself has no dependencies and will
// never update. The lack of dependencies that change is what confirms
// that this effect only runs on mount/unmount
useEffect(() => {
return teardownTracker;
}, [teardownTracker]);
// Effect to set loading state and initialize tracker when values change
useEffect(() => {
// This effect will only run initially and when:
// 1. chainId is updated,
// 2. userAddress is changed,
// 3. token list is updated and not equal to previous list
// in any of these scenarios, we should indicate to the user that their token
// values are in the process of updating by setting loading state.
setLoading(true);
if (!userAddress || chainId === undefined || !global.ethereumProvider) {
// If we do not have enough information to build a TokenTracker, we exit early
// When the values above change, the effect will be restarted. We also teardown
// tracker because inevitably this effect will run again momentarily.
teardownTracker();
return;
}
if (memoizedTokens.length === 0) {
// sets loading state to false and token list to empty
updateBalances([]);
}
buildTracker(userAddress, memoizedTokens);
}, [
userAddress,
teardownTracker,
chainId,
memoizedTokens,
updateBalances,
buildTracker,
]);
return { loading, tokensWithBalances, error };
}