UNPKG

@replyke/core

Version:

Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.

79 lines 3.17 kB
import { useCallback, useState, useEffect } from "react"; import useAddReaction from "./useAddReaction"; import useRemoveReaction from "./useRemoveReaction"; import { handleError } from "../../utils/handleError"; function useReactionToggle({ targetType, targetId, initialReaction, initialReactionCounts, }) { const addReaction = useAddReaction(); const removeReaction = useRemoveReaction(); const [currentReaction, setCurrentReaction] = useState(initialReaction ?? null); const [reactionCounts, setReactionCounts] = useState(initialReactionCounts ?? {}); const [loading, setLoading] = useState(false); // Reset state when target changes useEffect(() => { setCurrentReaction(initialReaction ?? null); setReactionCounts(initialReactionCounts ?? {}); }, [targetId, initialReaction, initialReactionCounts]); const toggleReaction = useCallback(async (props) => { const { reactionType } = props; // Guard: prevent concurrent operations if (loading) return null; if (!targetId) return null; // Store original for revert on error const originalReaction = currentReaction; const originalCounts = { ...reactionCounts }; // Determine new reaction (null if same, reactionType if different) const newReaction = currentReaction === reactionType ? null : reactionType; // OPTIMISTIC: Update UI immediately setCurrentReaction(newReaction); // OPTIMISTIC: Update counts const updatedCounts = { ...reactionCounts }; if (currentReaction) { // Decrement old reaction count updatedCounts[currentReaction] = Math.max((updatedCounts[currentReaction] || 0) - 1, 0); } if (newReaction) { // Increment new reaction count updatedCounts[newReaction] = (updatedCounts[newReaction] || 0) + 1; } setReactionCounts(updatedCounts); try { setLoading(true); // Call backend const result = newReaction === null ? await removeReaction({ targetType, targetId }) : await addReaction({ targetType, targetId, reactionType }); // Update with server truth (may differ from optimistic) setCurrentReaction(result.userReaction ?? null); setReactionCounts(result.reactionCounts ? { ...result.reactionCounts } : {}); return result; } catch (err) { // REVERT: Restore original on error setCurrentReaction(originalReaction); setReactionCounts(originalCounts); handleError(err, "Failed to toggle reaction:"); return null; } finally { setLoading(false); } }, [ loading, currentReaction, reactionCounts, targetType, targetId, addReaction, removeReaction, ]); return { currentReaction, reactionCounts, toggleReaction, loading, }; } export default useReactionToggle; //# sourceMappingURL=useReactionToggle.js.map