Skip to content

Commit

Permalink
Prevent rerenders of the AccountListItem
Browse files Browse the repository at this point in the history
  • Loading branch information
darkwing committed Mar 7, 2025
1 parent a0b8be9 commit 90b6a1d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 68 deletions.
12 changes: 7 additions & 5 deletions ui/components/multichain/account-list-item/account-list-item.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta)
Expand Down Expand Up @@ -163,10 +163,12 @@ const AccountListItem = ({
formattedTokensWithBalancesPerChain,
);
// cross chain agg balance
const mappedOrderedTokenList = accountTotalFiatBalances.orderedTokenList.map(
(item) => ({
avatarValue: item.iconUrl,
}),
const mappedOrderedTokenList = useMemo(
() =>
accountTotalFiatBalances.orderedTokenList.map((item) => ({
avatarValue: item.iconUrl,
})),
[accountTotalFiatBalances.orderedTokenList],
);
let balanceToTranslate;
if (isEvmNetwork) {
Expand Down
128 changes: 72 additions & 56 deletions ui/components/multichain/account-list-menu/account-list-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,19 +312,23 @@ export const AccountListMenu = ({
);
///: END:ONLY_INCLUDE_IF

let searchResults: MergedInternalAccount[] = filteredUpdatedAccountList;
if (searchQuery) {
const fuse = new Fuse(filteredAccounts, {
threshold: 0.2,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: ['metadata.name', 'address'],
});
fuse.setCollection(filteredAccounts);
searchResults = fuse.search(searchQuery);
}
const searchResults: MergedInternalAccount[] = useMemo(() => {
let _searchResults: MergedInternalAccount[] = filteredUpdatedAccountList;
if (searchQuery) {
const fuse = new Fuse(filteredAccounts, {
threshold: 0.2,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: ['metadata.name', 'address'],
});
fuse.setCollection(filteredAccounts);
_searchResults = fuse.search(searchQuery);
}

return _searchResults;
}, [filteredAccounts, filteredUpdatedAccountList, searchQuery]);

const title = useMemo(
() => getActionTitle(t as (text: string) => string, actionMode),
Expand All @@ -342,7 +346,7 @@ export const AccountListMenu = ({
}

const onAccountListItemItemClicked = useCallback(
(account) => {
(account: MergedInternalAccount) => {
return () => {
onClose();
trackEvent({
Expand All @@ -365,9 +369,59 @@ export const AccountListMenu = ({
dispatch(setSelectedAccount(account.address));
};
},
[dispatch, onClose, trackEvent],
[dispatch, onClose, trackEvent, defaultHomeActiveTabName],
);

const accountListItems = useMemo(() => {
return searchResults.map((account) => {
const connectedSite = connectedSites[account.address]?.find(
({ origin }) => origin === currentTabOrigin,
);

const hideAccountListItem = searchQuery.length === 0 && account.hidden;

/* NOTE: Hidden account will be displayed only in the search list */

return (
<Box
className={
account.hidden
? 'multichain-account-menu-popover__list--menu-item-hidden'
: 'multichain-account-menu-popover__list--menu-item'
}
display={hideAccountListItem ? Display.None : Display.Block}
key={account.address}
>
<AccountListItem
onClick={onAccountListItemItemClicked}
account={account}
key={account.address}
selected={selectedAccount.address === account.address}
closeMenu={onClose}
connectedAvatar={connectedSite?.iconUrl}
menuType={AccountListItemMenuTypes.Account}
isPinned={Boolean(account.pinned)}
isHidden={Boolean(account.hidden)}
currentTabOrigin={currentTabOrigin}
isActive={Boolean(account.active)}
privacyMode={privacyMode}
{...accountListItemProps}
/>
</Box>
);
});
}, [
searchResults,
connectedSites,
currentTabOrigin,
privacyMode,
accountListItemProps,
selectedAccount,
onClose,
onAccountListItemItemClicked,
searchQuery,
]);

return (
<Modal isOpen onClose={onClose}>
<ModalOverlay />
Expand Down Expand Up @@ -713,9 +767,8 @@ export const AccountListMenu = ({
size: Size.SM,
}}
inputProps={{ autoFocus: true }}
// TODO: These props are required in the TextFieldSearch component. These should be optional
endAccessory
className
endAccessory={null}
className=""
/>
</Box>
) : null}
Expand All @@ -731,44 +784,7 @@ export const AccountListMenu = ({
{t('noAccountsFound')}
</Text>
) : null}
{searchResults.map((account) => {
const connectedSite = connectedSites[account.address]?.find(
({ origin }) => origin === currentTabOrigin,
);

const hideAccountListItem =
searchQuery.length === 0 && account.hidden;

/* NOTE: Hidden account will be displayed only in the search list */

return (
<Box
className={
account.hidden
? 'multichain-account-menu-popover__list--menu-item-hidden'
: 'multichain-account-menu-popover__list--menu-item'
}
display={hideAccountListItem ? Display.None : Display.Block}
key={account.address}
>
<AccountListItem
onClick={onAccountListItemItemClicked(account)}
account={account}
key={account.address}
selected={selectedAccount.address === account.address}
closeMenu={onClose}
connectedAvatar={connectedSite?.iconUrl}
menuType={AccountListItemMenuTypes.Account}
isPinned={Boolean(account.pinned)}
isHidden={Boolean(account.hidden)}
currentTabOrigin={currentTabOrigin}
isActive={Boolean(account.active)}
privacyMode={privacyMode}
{...accountListItemProps}
/>
</Box>
);
})}
{accountListItems}
</Box>
{/* Hidden Accounts, this component shows hidden accounts in account list Item*/}
{hiddenAddresses.length > 0 ? (
Expand Down
2 changes: 1 addition & 1 deletion ui/pages/routes/routes.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ export default class Routes extends Component {
{showOnboardingHeader(location) && <OnboardingAppHeader />}
{isAccountMenuOpen ? (
<AccountListMenu
onClose={() => toggleAccountMenu()}
onClose={toggleAccountMenu}
privacyMode={privacyMode}
/>
) : null}
Expand Down
9 changes: 6 additions & 3 deletions ui/selectors/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@metamask/keyring-api';
import { InternalAccount } from '@metamask/keyring-internal-api';
import { AccountsControllerState } from '@metamask/accounts-controller';
import { createSelector } from 'reselect';
import {
isBtcMainnetAddress,
isBtcTestnetAddress,
Expand All @@ -26,9 +27,11 @@ export function isSolanaAccount(account: InternalAccount) {
return Boolean(account && account.type === DataAccount);
}

export function getInternalAccounts(state: AccountsState) {
return Object.values(state.metamask.internalAccounts.accounts);
}
export const getInternalAccounts = createSelector(
(state: AccountsState) =>
Object.values(state.metamask.internalAccounts.accounts),
(accounts) => accounts,
);

export function getSelectedInternalAccount(state: AccountsState) {
const accountId = state.metamask.internalAccounts.selectedAccount;
Expand Down
6 changes: 3 additions & 3 deletions ui/selectors/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export function getAccountTypeForKeyring(keyring) {
/**
* Get MetaMask accounts, including account name and balance.
*/
export const getMetaMaskAccounts = createSelector(
export const getMetaMaskAccounts = createDeepEqualSelector(
getInternalAccounts,
getMetaMaskAccountBalances,
getMetaMaskCachedBalances,
Expand Down Expand Up @@ -501,7 +501,7 @@ export const getSelectedEvmInternalAccount = createSelector(
* @param accounts - The object containing the accounts.
* @returns The array of internal accounts sorted by keyring.
*/
export const getInternalAccountsSortedByKeyring = createSelector(
export const getInternalAccountsSortedByKeyring = createDeepEqualSelector(
getMetaMaskKeyrings,
getMetaMaskAccounts,
(keyrings, accounts) => {
Expand Down Expand Up @@ -797,7 +797,7 @@ function getNativeTokenInfo(state, chainId) {
*
* @returns {InternalAccountWithBalance} An array of internal accounts with balance
*/
export const getMetaMaskAccountsOrdered = createSelector(
export const getMetaMaskAccountsOrdered = createDeepEqualSelector(
getInternalAccountsSortedByKeyring,
getMetaMaskAccounts,
(internalAccounts, accounts) => {
Expand Down

0 comments on commit 90b6a1d

Please sign in to comment.