diff --git a/ui/app/hooks/useEqualityCheck.js b/ui/app/hooks/useEqualityCheck.js new file mode 100644 index 000000000..ee4ca1e0a --- /dev/null +++ b/ui/app/hooks/useEqualityCheck.js @@ -0,0 +1,27 @@ +import { useState, useLayoutEffect } from 'react' + +import { isEqual } from 'lodash' + +/** + * Given a value and a function to determine equality, return a + * referentially equal value if the equality function returns true. + * This hook is helpful in avoiding re-renders and effects running + * based on an object or value that always changes references but + * infrequently changes it's value. By default, uses isEqual from + * lodash. This is typically only useful with objects and arrays. + * + * @param {T} value - any value to check equality of + * @param {(T, T) => boolean} equalityFn - A function to determine equality + * @returns {T} + */ +export function useEqualityCheck (value, equalityFn = isEqual) { + const [computedValue, setComputedValue] = useState(value) + + useLayoutEffect(() => { + if (!equalityFn(value, computedValue)) { + setComputedValue(value) + } + }, [value, equalityFn, computedValue]) + + return computedValue +} diff --git a/ui/app/hooks/useTokenTracker.js b/ui/app/hooks/useTokenTracker.js index 55a92273b..65671cd00 100644 --- a/ui/app/hooks/useTokenTracker.js +++ b/ui/app/hooks/useTokenTracker.js @@ -2,6 +2,7 @@ import { useState, useEffect, useRef, useCallback } from 'react' import TokenTracker from '@metamask/eth-token-tracker' import { useSelector } from 'react-redux' import { getCurrentNetwork, getSelectedAddress } from '../selectors' +import { useEqualityCheck } from './useEqualityCheck' export function useTokenTracker (tokens) { const network = useSelector(getCurrentNetwork) @@ -11,6 +12,7 @@ export function useTokenTracker (tokens) { const [tokensWithBalances, setTokensWithBalances] = useState([]) const [error, setError] = useState(null) const tokenTracker = useRef(null) + const memoizedTokens = useEqualityCheck(tokens) const updateBalances = useCallback((tokenWithBalances) => { setTokensWithBalances(tokenWithBalances) @@ -74,13 +76,13 @@ export function useTokenTracker (tokens) { return } - if (tokens.length === 0) { + if (memoizedTokens.length === 0) { // sets loading state to false and token list to empty updateBalances([]) } - buildTracker(userAddress, tokens) - }, [userAddress, teardownTracker, network, tokens, updateBalances, buildTracker]) + buildTracker(userAddress, memoizedTokens) + }, [userAddress, teardownTracker, network, memoizedTokens, updateBalances, buildTracker]) return { loading, tokensWithBalances, error } }