diff --git a/lib/shared/search-utils.js b/lib/shared/search-utils.js
--- a/lib/shared/search-utils.js
+++ b/lib/shared/search-utils.js
@@ -23,6 +23,7 @@
   ChatMessageInfoItem,
   MessageListData,
 } from '../selectors/chat-selectors.js';
+import { relationshipBlockedInEitherDirection } from '../shared/relationship-utils.js';
 import type { MessageInfo, RawMessageInfo } from '../types/message-types.js';
 import { userRelationshipStatus } from '../types/relationship-types.js';
 import { threadPermissions } from '../types/thread-permission-types.js';
@@ -43,7 +44,49 @@
 
 const notFriendNotice = 'not friend';
 
-function getPotentialMemberItems({
+function appendUserInfo({
+  results,
+  excludeUserIDs,
+  userInfo,
+  parentThreadInfo,
+  communityThreadInfo,
+  containingThreadInfo,
+}: {
+  +results: {
+    [id: string]: {
+      ...AccountUserInfo | GlobalAccountUserInfo,
+      isMemberOfParentThread: boolean,
+      isMemberOfContainingThread: boolean,
+    },
+  },
+  +excludeUserIDs: $ReadOnlyArray<string>,
+  +userInfo: AccountUserInfo | GlobalAccountUserInfo,
+  +parentThreadInfo: ?ThreadInfo,
+  +communityThreadInfo: ?ThreadInfo,
+  +containingThreadInfo: ?ThreadInfo,
+}) {
+  const { id } = userInfo;
+  if (excludeUserIDs.includes(id) || id in results) {
+    return;
+  }
+  if (
+    communityThreadInfo &&
+    !threadMemberHasPermission(
+      communityThreadInfo,
+      id,
+      threadPermissions.KNOW_OF,
+    )
+  ) {
+    return;
+  }
+  results[id] = {
+    ...userInfo,
+    isMemberOfParentThread: userIsMember(parentThreadInfo, id),
+    isMemberOfContainingThread: userIsMember(containingThreadInfo, id),
+  };
+}
+
+function usePotentialMemberItems({
   text,
   userInfos,
   searchIndex,
@@ -62,185 +105,202 @@
   +inputCommunityThreadInfo?: ?ThreadInfo,
   +threadType?: ?ThreadType,
 }): UserListItem[] {
-  const communityThreadInfo =
-    inputCommunityThreadInfo && inputCommunityThreadInfo.id !== genesis.id
-      ? inputCommunityThreadInfo
-      : null;
-  const parentThreadInfo =
-    inputParentThreadInfo && inputParentThreadInfo.id !== genesis.id
-      ? inputParentThreadInfo
-      : null;
+  const communityThreadInfo = React.useMemo(
+    () =>
+      inputCommunityThreadInfo && inputCommunityThreadInfo.id !== genesis.id
+        ? inputCommunityThreadInfo
+        : null,
+    [inputCommunityThreadInfo],
+  );
+  const parentThreadInfo = React.useMemo(
+    () =>
+      inputParentThreadInfo && inputParentThreadInfo.id !== genesis.id
+        ? inputParentThreadInfo
+        : null,
+    [inputParentThreadInfo],
+  );
 
   const containgThreadID = threadType
     ? getContainingThreadID(parentThreadInfo, threadType)
     : null;
 
-  let containingThreadInfo = null;
-  if (containgThreadID === parentThreadInfo?.id) {
-    containingThreadInfo = parentThreadInfo;
-  } else if (containgThreadID === communityThreadInfo?.id) {
-    containingThreadInfo = communityThreadInfo;
-  }
-
-  const results: {
-    [id: string]: {
-      ...AccountUserInfo | GlobalAccountUserInfo,
-      isMemberOfParentThread: boolean,
-      isMemberOfContainingThread: boolean,
-    },
-  } = {};
-  const appendUserInfo = (
-    userInfo: AccountUserInfo | GlobalAccountUserInfo,
-  ) => {
-    const { id } = userInfo;
-    if (excludeUserIDs.includes(id) || id in results) {
-      return;
-    }
-    if (
-      communityThreadInfo &&
-      !threadMemberHasPermission(
-        communityThreadInfo,
-        id,
-        threadPermissions.KNOW_OF,
-      )
-    ) {
-      return;
+  const containingThreadInfo = React.useMemo(() => {
+    if (containgThreadID === parentThreadInfo?.id) {
+      return parentThreadInfo;
+    } else if (containgThreadID === communityThreadInfo?.id) {
+      return communityThreadInfo;
     }
-    results[id] = {
-      ...userInfo,
-      isMemberOfParentThread: userIsMember(parentThreadInfo, id),
-      isMemberOfContainingThread: userIsMember(containingThreadInfo, id),
-    };
-  };
-  if (text === '') {
-    for (const id in userInfos) {
-      appendUserInfo(userInfos[id]);
+    return null;
+  }, [containgThreadID, communityThreadInfo, parentThreadInfo]);
+
+  const filteredUserResults = React.useMemo(() => {
+    const results: {
+      [id: string]: {
+        ...AccountUserInfo | GlobalAccountUserInfo,
+        isMemberOfParentThread: boolean,
+        isMemberOfContainingThread: boolean,
+      },
+    } = {};
+    if (text === '') {
+      for (const id in userInfos) {
+        appendUserInfo({
+          results,
+          excludeUserIDs,
+          userInfo: userInfos[id],
+          parentThreadInfo,
+          communityThreadInfo,
+          containingThreadInfo,
+        });
+      }
+    } else {
+      const ids = searchIndex.getSearchResults(text);
+      for (const id of ids) {
+        appendUserInfo({
+          results,
+          excludeUserIDs,
+          userInfo: userInfos[id],
+          parentThreadInfo,
+          communityThreadInfo,
+          containingThreadInfo,
+        });
+      }
     }
-  } else {
-    const ids = searchIndex.getSearchResults(text);
-    for (const id of ids) {
-      appendUserInfo(userInfos[id]);
+
+    if (includeServerSearchUsers) {
+      for (const userInfo of includeServerSearchUsers) {
+        appendUserInfo({
+          results,
+          excludeUserIDs,
+          userInfo,
+          parentThreadInfo,
+          communityThreadInfo,
+          containingThreadInfo,
+        });
+      }
     }
-  }
 
-  if (includeServerSearchUsers) {
-    for (const userInfo of includeServerSearchUsers) {
-      appendUserInfo(userInfo);
+    let userResults = values(results);
+    if (text === '') {
+      userResults = userResults.filter(userInfo => {
+        if (!containingThreadInfo) {
+          return userInfo.relationshipStatus === userRelationshipStatus.FRIEND;
+        }
+        if (!userInfo.isMemberOfContainingThread) {
+          return false;
+        }
+        const { relationshipStatus } = userInfo;
+        if (!relationshipStatus) {
+          return true;
+        }
+        return !relationshipBlockedInEitherDirection(relationshipStatus);
+      });
     }
-  }
 
-  const blockedRelationshipsStatuses = new Set([
-    userRelationshipStatus.BLOCKED_BY_VIEWER,
-    userRelationshipStatus.BLOCKED_VIEWER,
-    userRelationshipStatus.BOTH_BLOCKED,
+    return userResults;
+  }, [
+    text,
+    userInfos,
+    searchIndex,
+    excludeUserIDs,
+    includeServerSearchUsers,
+    parentThreadInfo,
+    containingThreadInfo,
+    communityThreadInfo,
   ]);
 
-  let userResults = values(results);
-  if (text === '') {
-    userResults = userResults.filter(userInfo => {
-      if (!containingThreadInfo) {
-        return userInfo.relationshipStatus === userRelationshipStatus.FRIEND;
-      }
-      if (!userInfo.isMemberOfContainingThread) {
-        return false;
-      }
-      const { relationshipStatus } = userInfo;
-      if (!relationshipStatus) {
-        return true;
-      }
-      return !blockedRelationshipsStatuses.has(relationshipStatus);
-    });
-  }
-
-  const nonFriends = [];
-  const blockedUsers = [];
-  const friends = [];
-  const containingThreadMembers = [];
-  const parentThreadMembers = [];
-
-  for (const userResult of userResults) {
-    const { relationshipStatus } = userResult;
-    if (
-      relationshipStatus &&
-      blockedRelationshipsStatuses.has(relationshipStatus)
-    ) {
-      blockedUsers.push(userResult);
-    } else if (userResult.isMemberOfParentThread) {
-      parentThreadMembers.push(userResult);
-    } else if (userResult.isMemberOfContainingThread) {
-      containingThreadMembers.push(userResult);
-    } else if (relationshipStatus === userRelationshipStatus.FRIEND) {
-      friends.push(userResult);
-    } else {
-      nonFriends.push(userResult);
-    }
-  }
+  const sortedMembers = React.useMemo(() => {
+    const nonFriends = [];
+    const blockedUsers = [];
+    const friends = [];
+    const containingThreadMembers = [];
+    const parentThreadMembers = [];
 
-  const sortedResults = parentThreadMembers
-    .concat(containingThreadMembers)
-    .concat(friends)
-    .concat(nonFriends)
-    .concat(blockedUsers);
-
-  return sortedResults.map(
-    ({
-      isMemberOfContainingThread,
-      isMemberOfParentThread,
-      relationshipStatus,
-      ...result
-    }) => {
-      let notice, alert;
-      const username = result.username;
+    for (const userResult of filteredUserResults) {
+      const { relationshipStatus } = userResult;
       if (
         relationshipStatus &&
-        blockedRelationshipsStatuses.has(relationshipStatus)
+        relationshipBlockedInEitherDirection(relationshipStatus)
       ) {
-        notice = 'user is blocked';
-        alert = {
-          title: 'User is blocked',
-          text:
-            `Before you add ${username} to this chat, ` +
-            'you’ll need to unblock them. You can do this from the Block List ' +
-            'in the Profile tab.',
-        };
-      } else if (!isMemberOfContainingThread && containingThreadInfo) {
-        if (threadType !== threadTypes.SIDEBAR) {
-          notice = 'not in community';
+        blockedUsers.push(userResult);
+      } else if (userResult.isMemberOfParentThread) {
+        parentThreadMembers.push(userResult);
+      } else if (userResult.isMemberOfContainingThread) {
+        containingThreadMembers.push(userResult);
+      } else if (relationshipStatus === userRelationshipStatus.FRIEND) {
+        friends.push(userResult);
+      } else {
+        nonFriends.push(userResult);
+      }
+    }
+
+    const sortedResults = parentThreadMembers
+      .concat(containingThreadMembers)
+      .concat(friends)
+      .concat(nonFriends)
+      .concat(blockedUsers);
+
+    return sortedResults.map(
+      ({
+        isMemberOfContainingThread,
+        isMemberOfParentThread,
+        relationshipStatus,
+        ...result
+      }) => {
+        let notice, alert;
+        const username = result.username;
+        if (
+          relationshipStatus &&
+          relationshipBlockedInEitherDirection(relationshipStatus)
+        ) {
+          notice = 'user is blocked';
           alert = {
-            title: 'Not in community',
-            text: 'You can only add members of the community to this chat',
+            title: 'User is blocked',
+            text:
+              `Before you add ${username} to this chat, ` +
+              'you’ll need to unblock them. You can do this from the Block List ' +
+              'in the Profile tab.',
           };
-        } else {
-          notice = 'not in parent chat';
+        } else if (!isMemberOfContainingThread && containingThreadInfo) {
+          if (threadType !== threadTypes.SIDEBAR) {
+            notice = 'not in community';
+            alert = {
+              title: 'Not in community',
+              text: 'You can only add members of the community to this chat',
+            };
+          } else {
+            notice = 'not in parent chat';
+            alert = {
+              title: 'Not in parent chat',
+              text: 'You can only add members of the parent chat to a thread',
+            };
+          }
+        } else if (
+          !containingThreadInfo &&
+          relationshipStatus !== userRelationshipStatus.FRIEND
+        ) {
+          notice = notFriendNotice;
           alert = {
-            title: 'Not in parent chat',
-            text: 'You can only add members of the parent chat to a thread',
+            title: 'Not a friend',
+            text:
+              `Before you add ${username} to this chat, ` +
+              'you’ll need to send them a friend request. ' +
+              'You can do this from the Friend List in the Profile tab.',
           };
+        } else if (parentThreadInfo && !isMemberOfParentThread) {
+          notice = 'not in parent chat';
         }
-      } else if (
-        !containingThreadInfo &&
-        relationshipStatus !== userRelationshipStatus.FRIEND
-      ) {
-        notice = notFriendNotice;
-        alert = {
-          title: 'Not a friend',
-          text:
-            `Before you add ${username} to this chat, ` +
-            'you’ll need to send them a friend request. ' +
-            'You can do this from the Friend List in the Profile tab.',
-        };
-      } else if (parentThreadInfo && !isMemberOfParentThread) {
-        notice = 'not in parent chat';
-      }
-      if (notice) {
-        result = { ...result, notice };
-      }
-      if (alert) {
-        result = { ...result, alert };
-      }
-      return result;
-    },
-  );
+        if (notice) {
+          result = { ...result, notice };
+        }
+        if (alert) {
+          result = { ...result, alert };
+        }
+        return result;
+      },
+    );
+  }, [containingThreadInfo, filteredUserResults, parentThreadInfo, threadType]);
+
+  return sortedMembers;
 }
 
 function useSearchMessages(): (
@@ -382,7 +442,7 @@
 }
 
 export {
-  getPotentialMemberItems,
+  usePotentialMemberItems,
   notFriendNotice,
   useSearchMessages,
   useSearchUsers,
diff --git a/native/chat/compose-subchannel.react.js b/native/chat/compose-subchannel.react.js
--- a/native/chat/compose-subchannel.react.js
+++ b/native/chat/compose-subchannel.react.js
@@ -17,7 +17,7 @@
   userInfoSelectorForPotentialMembers,
   userSearchIndexForPotentialMembers,
 } from 'lib/selectors/user-selectors.js';
-import { getPotentialMemberItems } from 'lib/shared/search-utils.js';
+import { usePotentialMemberItems } from 'lib/shared/search-utils.js';
 import { threadInFilterList, userIsMember } from 'lib/shared/thread-utils.js';
 import { type ThreadType, threadTypes } from 'lib/types/thread-types-enum.js';
 import type { ThreadInfo } from 'lib/types/thread-types.js';
@@ -200,27 +200,15 @@
   const communityThreadInfo = useSelector(state =>
     community ? threadInfoSelector(state)[community] : null,
   );
-  const userSearchResults = React.useMemo(
-    () =>
-      getPotentialMemberItems({
-        text: usernameInputText,
-        userInfos: otherUserInfos,
-        searchIndex: userSearchIndex,
-        excludeUserIDs: userInfoInputIDs,
-        inputParentThreadInfo: parentThreadInfo,
-        inputCommunityThreadInfo: communityThreadInfo,
-        threadType,
-      }),
-    [
-      usernameInputText,
-      otherUserInfos,
-      userSearchIndex,
-      userInfoInputIDs,
-      parentThreadInfo,
-      communityThreadInfo,
-      threadType,
-    ],
-  );
+  const userSearchResults = usePotentialMemberItems({
+    text: usernameInputText,
+    userInfos: otherUserInfos,
+    searchIndex: userSearchIndex,
+    excludeUserIDs: userInfoInputIDs,
+    inputParentThreadInfo: parentThreadInfo,
+    inputCommunityThreadInfo: communityThreadInfo,
+    threadType,
+  });
 
   const existingThreads: $ReadOnlyArray<ThreadInfo> = React.useMemo(() => {
     if (userInfoInputIDs.length === 0) {
diff --git a/native/chat/message-list-container.react.js b/native/chat/message-list-container.react.js
--- a/native/chat/message-list-container.react.js
+++ b/native/chat/message-list-container.react.js
@@ -13,7 +13,7 @@
   userSearchIndexForPotentialMembers,
 } from 'lib/selectors/user-selectors.js';
 import {
-  getPotentialMemberItems,
+  usePotentialMemberItems,
   useSearchUsers,
 } from 'lib/shared/search-utils.js';
 import {
@@ -253,21 +253,13 @@
 
     const serverSearchResults = useSearchUsers(usernameInputText);
 
-    const userSearchResults = React.useMemo(() => {
-      return getPotentialMemberItems({
-        text: usernameInputText,
-        userInfos: otherUserInfos,
-        searchIndex: userSearchIndex,
-        excludeUserIDs: userInfoInputArray.map(userInfo => userInfo.id),
-        includeServerSearchUsers: serverSearchResults,
-      });
-    }, [
-      usernameInputText,
-      otherUserInfos,
-      userSearchIndex,
-      userInfoInputArray,
-      serverSearchResults,
-    ]);
+    const userSearchResults = usePotentialMemberItems({
+      text: usernameInputText,
+      userInfos: otherUserInfos,
+      searchIndex: userSearchIndex,
+      excludeUserIDs: userInfoInputArray.map(userInfo => userInfo.id),
+      includeServerSearchUsers: serverSearchResults,
+    });
 
     const [baseThreadInfo, setBaseThreadInfo] = React.useState(
       props.route.params.threadInfo,
diff --git a/native/chat/settings/add-users-modal.react.js b/native/chat/settings/add-users-modal.react.js
--- a/native/chat/settings/add-users-modal.react.js
+++ b/native/chat/settings/add-users-modal.react.js
@@ -14,7 +14,7 @@
   userInfoSelectorForPotentialMembers,
   userSearchIndexForPotentialMembers,
 } from 'lib/selectors/user-selectors.js';
-import { getPotentialMemberItems } from 'lib/shared/search-utils.js';
+import { usePotentialMemberItems } from 'lib/shared/search-utils.js';
 import { threadActualMembers } from 'lib/shared/thread-utils.js';
 import type { ThreadInfo } from 'lib/types/thread-types.js';
 import { type AccountUserInfo } from 'lib/types/user-types.js';
@@ -173,27 +173,15 @@
   const communityThreadInfo = useSelector(state =>
     community ? threadInfoSelector(state)[community] : null,
   );
-  const userSearchResults = React.useMemo(
-    () =>
-      getPotentialMemberItems({
-        text: usernameInputText,
-        userInfos: otherUserInfos,
-        searchIndex: userSearchIndex,
-        excludeUserIDs,
-        inputParentThreadInfo: parentThreadInfo,
-        inputCommunityThreadInfo: communityThreadInfo,
-        threadType: threadInfo.type,
-      }),
-    [
-      usernameInputText,
-      otherUserInfos,
-      userSearchIndex,
-      excludeUserIDs,
-      parentThreadInfo,
-      communityThreadInfo,
-      threadInfo.type,
-    ],
-  );
+  const userSearchResults = usePotentialMemberItems({
+    text: usernameInputText,
+    userInfos: otherUserInfos,
+    searchIndex: userSearchIndex,
+    excludeUserIDs,
+    inputParentThreadInfo: parentThreadInfo,
+    inputCommunityThreadInfo: communityThreadInfo,
+    threadType: threadInfo.type,
+  });
 
   const onChangeTagInput = React.useCallback(
     (newUserInfoInputArray: $ReadOnlyArray<AccountUserInfo>) => {
diff --git a/native/community-creation/community-creation-members.react.js b/native/community-creation/community-creation-members.react.js
--- a/native/community-creation/community-creation-members.react.js
+++ b/native/community-creation/community-creation-members.react.js
@@ -13,7 +13,7 @@
   userInfoSelectorForPotentialMembers,
   userSearchIndexForPotentialMembers,
 } from 'lib/selectors/user-selectors.js';
-import { getPotentialMemberItems } from 'lib/shared/search-utils.js';
+import { usePotentialMemberItems } from 'lib/shared/search-utils.js';
 import type { LoadingStatus } from 'lib/types/loading-types.js';
 import { threadTypes } from 'lib/types/thread-types-enum.js';
 import type { AccountUserInfo } from 'lib/types/user-types.js';
@@ -144,25 +144,15 @@
     setOptions,
   ]);
 
-  const userSearchResults = React.useMemo(
-    () =>
-      getPotentialMemberItems({
-        text: usernameInputText,
-        userInfos: otherUserInfos,
-        searchIndex: userSearchIndex,
-        excludeUserIDs: selectedUserIDs,
-        threadType: announcement
-          ? threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT
-          : threadTypes.COMMUNITY_ROOT,
-      }),
-    [
-      announcement,
-      otherUserInfos,
-      selectedUserIDs,
-      userSearchIndex,
-      usernameInputText,
-    ],
-  );
+  const userSearchResults = usePotentialMemberItems({
+    text: usernameInputText,
+    userInfos: otherUserInfos,
+    searchIndex: userSearchIndex,
+    excludeUserIDs: selectedUserIDs,
+    threadType: announcement
+      ? threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT
+      : threadTypes.COMMUNITY_ROOT,
+  });
 
   const onSelectUser = React.useCallback(
     ({ id }: AccountUserInfo) => {
diff --git a/web/chat/chat-thread-composer.react.js b/web/chat/chat-thread-composer.react.js
--- a/web/chat/chat-thread-composer.react.js
+++ b/web/chat/chat-thread-composer.react.js
@@ -11,7 +11,7 @@
 import { useENSNames } from 'lib/hooks/ens-cache.js';
 import { userSearchIndexForPotentialMembers } from 'lib/selectors/user-selectors.js';
 import {
-  getPotentialMemberItems,
+  usePotentialMemberItems,
   useSearchUsers,
   notFriendNotice,
 } from 'lib/shared/search-utils.js';
@@ -60,23 +60,13 @@
 
   const serverSearchResults = useSearchUsers(usernameInputText);
 
-  const userListItems = React.useMemo(
-    () =>
-      getPotentialMemberItems({
-        text: usernameInputText,
-        userInfos: otherUserInfos,
-        searchIndex: userSearchIndex,
-        excludeUserIDs: userInfoInputIDs,
-        includeServerSearchUsers: serverSearchResults,
-      }),
-    [
-      usernameInputText,
-      otherUserInfos,
-      userSearchIndex,
-      userInfoInputIDs,
-      serverSearchResults,
-    ],
-  );
+  const userListItems = usePotentialMemberItems({
+    text: usernameInputText,
+    userInfos: otherUserInfos,
+    searchIndex: userSearchIndex,
+    excludeUserIDs: userInfoInputIDs,
+    includeServerSearchUsers: serverSearchResults,
+  });
 
   const userListItemsWithENSNames = useENSNames(userListItems);
 
diff --git a/web/modals/threads/members/add-members-modal.react.js b/web/modals/threads/members/add-members-modal.react.js
--- a/web/modals/threads/members/add-members-modal.react.js
+++ b/web/modals/threads/members/add-members-modal.react.js
@@ -12,7 +12,7 @@
   userSearchIndexForPotentialMembers,
   userInfoSelectorForPotentialMembers,
 } from 'lib/selectors/user-selectors.js';
-import { getPotentialMemberItems } from 'lib/shared/search-utils.js';
+import { usePotentialMemberItems } from 'lib/shared/search-utils.js';
 import { threadActualMembers } from 'lib/shared/thread-utils.js';
 import { useDispatchActionPromise } from 'lib/utils/action-utils.js';
 
@@ -54,27 +54,15 @@
     [pendingUsersToAdd, threadInfo.members],
   );
 
-  const userSearchResults = React.useMemo(
-    () =>
-      getPotentialMemberItems({
-        text: searchText,
-        userInfos: otherUserInfos,
-        searchIndex: userSearchIndex,
-        excludeUserIDs,
-        inputParentThreadInfo: parentThreadInfo,
-        inputCommunityThreadInfo: communityThreadInfo,
-        threadType: threadInfo.type,
-      }),
-    [
-      communityThreadInfo,
-      excludeUserIDs,
-      otherUserInfos,
-      parentThreadInfo,
-      searchText,
-      threadInfo.type,
-      userSearchIndex,
-    ],
-  );
+  const userSearchResults = usePotentialMemberItems({
+    text: searchText,
+    userInfos: otherUserInfos,
+    searchIndex: userSearchIndex,
+    excludeUserIDs,
+    inputParentThreadInfo: parentThreadInfo,
+    inputCommunityThreadInfo: communityThreadInfo,
+    threadType: threadInfo.type,
+  });
   const userSearchResultsWithENSNames = useENSNames(userSearchResults);
 
   const onSwitchUser = React.useCallback(