diff --git a/lib/shared/message-utils.js b/lib/shared/message-utils.js
--- a/lib/shared/message-utils.js
+++ b/lib/shared/message-utils.js
@@ -47,6 +47,7 @@
   ET,
   useEntityTextAsString,
 } from '../utils/entity-text.js';
+import { useSelector } from '../utils/redux-utils.js';
 
 const localIDPrefix = 'local';
 
@@ -636,6 +637,11 @@
   return ET`assigned ${affectedUsers} the "${roleName}" role`;
 }
 
+function useNextLocalID(): string {
+  const nextLocalID = useSelector(state => state.nextLocalID);
+  return `${localIDPrefix}${nextLocalID}`;
+}
+
 export {
   localIDPrefix,
   messageKey,
@@ -664,4 +670,5 @@
   getPinnedContentFromMessage,
   modifyItemForResultScreen,
   constructChangeRoleEntityText,
+  useNextLocalID,
 };
diff --git a/native/chat/chat-input-bar.react.js b/native/chat/chat-input-bar.react.js
--- a/native/chat/chat-input-bar.react.js
+++ b/native/chat/chat-input-bar.react.js
@@ -43,7 +43,7 @@
   getMentionsCandidates,
 } from 'lib/shared/mention-utils.js';
 import {
-  localIDPrefix,
+  useNextLocalID,
   trimMessage,
   useMessagePreview,
   messageKey,
@@ -154,7 +154,7 @@
   +joinThreadLoadingStatus: LoadingStatus,
   +threadCreationInProgress: boolean,
   +calendarQuery: () => CalendarQuery,
-  +nextLocalID: number,
+  +nextLocalID: string,
   +userInfos: UserInfos,
   +colors: Colors,
   +styles: typeof unboundStyles,
@@ -854,7 +854,7 @@
       return;
     }
 
-    const localID = `${localIDPrefix}${this.props.nextLocalID}`;
+    const localID = this.props.nextLocalID;
     const creatorID = this.props.viewerID;
     invariant(creatorID, 'should have viewer ID in order to send a message');
     invariant(
@@ -1231,7 +1231,7 @@
       navContext,
     }),
   );
-  const nextLocalID = useSelector(state => state.nextLocalID);
+  const nextLocalID = useNextLocalID();
   const userInfos = useSelector(state => state.userStore.userInfos);
 
   const styles = useStyles(unboundStyles);
diff --git a/native/chat/inline-engagement.react.js b/native/chat/inline-engagement.react.js
--- a/native/chat/inline-engagement.react.js
+++ b/native/chat/inline-engagement.react.js
@@ -11,7 +11,7 @@
 
 import type { ReactionInfo } from 'lib/selectors/chat-selectors.js';
 import { getInlineEngagementSidebarText } from 'lib/shared/inline-engagement-utils.js';
-import { localIDPrefix } from 'lib/shared/message-utils.js';
+import { useNextLocalID } from 'lib/shared/message-utils.js';
 import type { MessageInfo } from 'lib/types/message-types.js';
 import type { ThreadInfo } from 'lib/types/thread-types.js';
 
@@ -28,7 +28,6 @@
 import CommIcon from '../components/comm-icon.react.js';
 import GestureTouchableOpacity from '../components/gesture-touchable-opacity.react.js';
 import { MessageReactionsModalRouteName } from '../navigation/route-names.js';
-import { useSelector } from '../redux/redux-utils.js';
 import { useStyles } from '../themes/colors.js';
 import type { ChatMessageInfoItemWithHeight } from '../types/chat-types.js';
 
@@ -247,8 +246,7 @@
     repliesText,
   ]);
 
-  const nextLocalID = useSelector(state => state.nextLocalID);
-  const localID = `${localIDPrefix}${nextLocalID}`;
+  const localID = useNextLocalID();
 
   const sendReaction = useSendReaction(
     messageInfo.id,
diff --git a/native/chat/multimedia-message-tooltip-button.react.js b/native/chat/multimedia-message-tooltip-button.react.js
--- a/native/chat/multimedia-message-tooltip-button.react.js
+++ b/native/chat/multimedia-message-tooltip-button.react.js
@@ -3,7 +3,7 @@
 import * as React from 'react';
 import Animated from 'react-native-reanimated';
 
-import { localIDPrefix } from 'lib/shared/message-utils.js';
+import { useNextLocalID } from 'lib/shared/message-utils.js';
 import {
   useViewerAlreadySelectedMessageReactions,
   useCanCreateReactionFromMessage,
@@ -103,8 +103,7 @@
   );
 
   const { messageInfo, threadInfo, reactions } = item;
-  const nextLocalID = useSelector(state => state.nextLocalID);
-  const localID = `${localIDPrefix}${nextLocalID}`;
+  const localID = useNextLocalID();
 
   const canCreateReactionFromMessage = useCanCreateReactionFromMessage(
     threadInfo,
diff --git a/native/chat/robotext-message-tooltip-button.react.js b/native/chat/robotext-message-tooltip-button.react.js
--- a/native/chat/robotext-message-tooltip-button.react.js
+++ b/native/chat/robotext-message-tooltip-button.react.js
@@ -3,7 +3,7 @@
 import * as React from 'react';
 import Animated from 'react-native-reanimated';
 
-import { localIDPrefix } from 'lib/shared/message-utils.js';
+import { useNextLocalID } from 'lib/shared/message-utils.js';
 import {
   useViewerAlreadySelectedMessageReactions,
   useCanCreateReactionFromMessage,
@@ -86,8 +86,7 @@
   }, [initialCoordinates, isOpeningSidebar, item, progress, windowWidth]);
 
   const { messageInfo, threadInfo, reactions } = item;
-  const nextLocalID = useSelector(state => state.nextLocalID);
-  const localID = `${localIDPrefix}${nextLocalID}`;
+  const localID = useNextLocalID();
 
   const canCreateReactionFromMessage = useCanCreateReactionFromMessage(
     threadInfo,
diff --git a/native/chat/text-message-tooltip-button.react.js b/native/chat/text-message-tooltip-button.react.js
--- a/native/chat/text-message-tooltip-button.react.js
+++ b/native/chat/text-message-tooltip-button.react.js
@@ -3,7 +3,7 @@
 import * as React from 'react';
 import Animated from 'react-native-reanimated';
 
-import { localIDPrefix } from 'lib/shared/message-utils.js';
+import { useNextLocalID } from 'lib/shared/message-utils.js';
 import {
   useViewerAlreadySelectedMessageReactions,
   useCanCreateReactionFromMessage,
@@ -100,8 +100,7 @@
   }, [initialCoordinates, isOpeningSidebar, item, progress, windowWidth]);
 
   const { messageInfo, threadInfo, reactions } = item;
-  const nextLocalID = useSelector(state => state.nextLocalID);
-  const localID = `${localIDPrefix}${nextLocalID}`;
+  const localID = useNextLocalID();
 
   const canCreateReactionFromMessage = useCanCreateReactionFromMessage(
     threadInfo,
diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js
--- a/native/input/input-state-container.react.js
+++ b/native/input/input-state-container.react.js
@@ -38,7 +38,7 @@
 } from 'lib/selectors/loading-selectors.js';
 import {
   createMediaMessageInfo,
-  localIDPrefix,
+  useNextLocalID,
   useMessageCreationSideEffectsFunc,
 } from 'lib/shared/message-utils.js';
 import type { CreationSideEffectsFunc } from 'lib/shared/messages/message-spec.js';
@@ -132,7 +132,7 @@
 type Props = {
   ...BaseProps,
   +viewerID: ?string,
-  +nextLocalID: number,
+  +nextLocalID: string,
   +messageStoreMessages: { +[id: string]: RawMessageInfo },
   +ongoingMessageCreation: boolean,
   +hasWiFi: boolean,
@@ -606,7 +606,7 @@
     threadInfo: ThreadInfo,
   ) => {
     this.sendCallbacks.forEach(callback => callback());
-    const localMessageID = `${localIDPrefix}${this.props.nextLocalID}`;
+    const localMessageID = this.props.nextLocalID;
     this.startThreadCreation(threadInfo);
 
     if (threadIsPendingSidebar(threadInfo.id)) {
@@ -1789,7 +1789,7 @@
     const viewerID = useSelector(
       state => state.currentUserInfo && state.currentUserInfo.id,
     );
-    const nextLocalID = useSelector(state => state.nextLocalID);
+    const nextLocalID = useNextLocalID();
     const messageStoreMessages = useSelector(
       state => state.messageStore.messages,
     );
diff --git a/web/chat/message-tooltip.react.js b/web/chat/message-tooltip.react.js
--- a/web/chat/message-tooltip.react.js
+++ b/web/chat/message-tooltip.react.js
@@ -6,7 +6,7 @@
 import * as React from 'react';
 
 import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
-import { localIDPrefix } from 'lib/shared/message-utils.js';
+import { useNextLocalID } from 'lib/shared/message-utils.js';
 import type { ThreadInfo } from 'lib/types/thread-types.js';
 
 import {
@@ -20,7 +20,6 @@
   getEmojiKeyboardPosition,
 } from './reaction-message-utils.js';
 import { useTooltipContext } from './tooltip-provider.js';
-import { useSelector } from '../redux/redux-utils.js';
 import type {
   MessageTooltipAction,
   TooltipSize,
@@ -170,8 +169,7 @@
     };
   }, [emojiKeyboardPosition]);
 
-  const nextLocalID = useSelector(state => state.nextLocalID);
-  const localID = `${localIDPrefix}${nextLocalID}`;
+  const localID = useNextLocalID();
 
   const sendReaction = useSendReaction(
     messageInfo.id,