@replyke/core
Version:
Replyke: Build interactive apps with social features like comments, votes, feeds, user lists, notifications, and more.
79 lines • 3.17 kB
JavaScript
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