useMessageReactions
Location: src/hooks/useMessageReactions.js
Overview
Manages reactions on messages within conversations. Uses a curated set of six reactions (heart, smile, thinking, lightbulb, hug, laugh) rather than an open emoji picker, keeping interactions intentional. Enforces one reaction per user per message -- toggling the same reaction removes it, selecting a different one replaces it.
The file also exports a REACTIONS constant array with the available reaction definitions.
Exported Constants
REACTIONS
const REACTIONS: Array<{
id: string, // e.g. "heart", "smile", "thinking", "lightbulb", "hug", "laugh"
emoji: string, // e.g. "❤️", "😊", "🤔", "💡", "🤗", "😄"
label: string // e.g. "Love", "Warm", "Thinking", "Great idea", "Support", "Haha"
}>
Signature
function useMessageReactions(): {
toggleReaction: (messageId: string, reactionId: string) => Promise<{ action?: string, reaction?: string, previous?: string, error?: string }>,
removeReaction: (messageId: string) => Promise<{ error?: PostgrestError | null }>,
fetchReactions: (messageIds: string[]) => Promise<Record<string, ReactionRow[]>>,
loading: Record<string, boolean>,
REACTIONS: ReactionDefinition[]
}
Parameters
None. Automatically resolves the current authenticated user.
Return Value
| Property | Type | Description |
|---|---|---|
toggleReaction | (messageId, reactionId) => Promise | Toggle a reaction on a message. If the user has no reaction, adds it (action: "added"). If the user has the same reaction, removes it (action: "removed"). If a different reaction, changes it (action: "changed", includes previous). Returns { error } on failure. |
removeReaction | (messageId) => Promise | Remove the current user's reaction from a message. Returns { error }. |
fetchReactions | (messageIds) => Promise | Fetch all reactions for a batch of messages. Returns an object keyed by message ID, with each value being an array of reaction rows (including user profile data). Returns empty object for empty input. |
loading | Record<string, boolean> | Object keyed by message ID, indicating whether a toggle operation is in progress for that message. |
REACTIONS | ReactionDefinition[] | The curated list of available reactions (same as the exported constant). |
Usage
function MessageReactionBar({ messageId }) {
const { toggleReaction, loading, REACTIONS } = useMessageReactions();
return (
<div className="reaction-bar">
{REACTIONS.map(reaction => (
<button
key={reaction.id}
onClick={() => toggleReaction(messageId, reaction.id)}
disabled={loading[messageId]}
aria-label={reaction.label}
>
{reaction.emoji}
</button>
))}
</div>
);
}
Fetching reactions for a message list
function MessageList({ messages }) {
const { fetchReactions } = useMessageReactions();
const [reactions, setReactions] = useState({});
useEffect(() => {
const ids = messages.map(m => m.id);
fetchReactions(ids).then(setReactions);
}, [messages]);
return messages.map(msg => (
<div key={msg.id}>
<p>{msg.content}</p>
<div className="reactions">
{(reactions[msg.id] || []).map(r => (
<span key={r.id}>{REACTIONS.find(x => x.id === r.reaction)?.emoji}</span>
))}
</div>
</div>
));
}
Last updated: 2026-02-07