Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions benchmarks/favorites_benchmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

// favorites_benchmark.ts

const NUM_RECIPES = 10000;
const NUM_FAVORITES = 1000;
const ITERATIONS = 1000;

// Generate mock data
const allRecipeIds = Array.from({ length: NUM_RECIPES }, (_, i) => `recipe-${i}`);
const favoriteIds = Array.from({ length: NUM_FAVORITES }, (_, i) => `recipe-${(i * 13) % NUM_RECIPES}`);

// Setup implementations
const favoritesArray = [...favoriteIds];

Check warning on line 13 in benchmarks/favorites_benchmark.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

`favoritesArray` should be a `Set`, and use `favoritesArray.has()` to check existence or non-existence.

See more on https://sonarcloud.io/project/issues?id=VarunB453_AHARA3&issues=AZznIv5_kU3N_5Om_0YZ&open=AZznIv5_kU3N_5Om_0YZ&pullRequest=5
const favoritesSet = new Set(favoriteIds);

console.log(`Benchmarking with ${NUM_RECIPES} recipes and ${NUM_FAVORITES} favorites over ${ITERATIONS} iterations.`);

// Benchmark Array.includes
const startArray = performance.now();
let arrayMatches = 0;
for (let i = 0; i < ITERATIONS; i++) {
for (const recipeId of allRecipeIds) {
if (favoritesArray.includes(recipeId)) {
arrayMatches++;
}
}
}
const endArray = performance.now();
const timeArray = endArray - startArray;

// Benchmark Set.has
const startSet = performance.now();
let setMatches = 0;
for (let i = 0; i < ITERATIONS; i++) {
for (const recipeId of allRecipeIds) {
if (favoritesSet.has(recipeId)) {
setMatches++;
}
}
}
const endSet = performance.now();
const timeSet = endSet - startSet;

console.log(`Array.includes time: ${timeArray.toFixed(2)}ms`);
console.log(`Set.has time: ${timeSet.toFixed(2)}ms`);
console.log(`Speedup: ${(timeArray / timeSet).toFixed(2)}x`);
38 changes: 21 additions & 17 deletions src/hooks/useFavorites.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useMemo } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { useAuth } from './useAuth';
import { useToast } from './use-toast';

export const useFavorites = () => {
const [favorites, setFavorites] = useState<string[]>([]);
const [favoritesSet, setFavoritesSet] = useState<Set<string>>(new Set());
const [loading, setLoading] = useState(false);
const { user } = useAuth();
const { toast } = useToast();
Expand All @@ -14,12 +14,12 @@ export const useFavorites = () => {
// Fetch user's favorites
const fetchFavorites = useCallback(async () => {
if (!user) {
setFavorites([]);
setFavoritesSet(new Set());
return;
}

if (!isValidUuid(user.id)) {
setFavorites([]);
setFavoritesSet(new Set());
return;
}

Expand All @@ -32,7 +32,7 @@ export const useFavorites = () => {

if (error) throw error;

setFavorites(data?.map(f => f.recipe_id) || []);
setFavoritesSet(new Set(data?.map(f => f.recipe_id) || []));
} catch (error) {
console.error('Error fetching favorites:', error);
} finally {
Expand All @@ -45,8 +45,8 @@ export const useFavorites = () => {
}, [fetchFavorites]);

const isFavorite = useCallback((recipeId: string) => {
return favorites.includes(recipeId);
}, [favorites]);
return favoritesSet.has(recipeId);
}, [favoritesSet]);

const toggleFavorite = useCallback(async (recipeId: string) => {
if (!user) {
Expand All @@ -67,7 +67,7 @@ export const useFavorites = () => {
return false;
}

const isCurrentlyFavorite = isFavorite(recipeId);
const isCurrentlyFavorite = favoritesSet.has(recipeId);

try {
if (isCurrentlyFavorite) {
Expand All @@ -80,7 +80,11 @@ export const useFavorites = () => {

if (error) throw error;

setFavorites(prev => prev.filter(id => id !== recipeId));
setFavoritesSet(prev => {
const next = new Set(prev);
next.delete(recipeId);
return next;
});
toast({
title: "Removed from favorites",
description: "Recipe removed from your favorites.",
Expand All @@ -105,12 +109,10 @@ export const useFavorites = () => {
throw error;
}

setFavorites(prev => {
// Only add if not already in favorites to prevent duplicates
if (!prev.includes(recipeId)) {
return [...prev, recipeId];
}
return prev;
setFavoritesSet(prev => {
const next = new Set(prev);
next.add(recipeId);
return next;
});
toast({
title: "Added to favorites",
Expand All @@ -127,10 +129,12 @@ export const useFavorites = () => {
});
return false;
}
}, [user, isFavorite, toast]);
}, [user, favoritesSet, toast, fetchFavorites]);

const favoritesArray = useMemo(() => Array.from(favoritesSet), [favoritesSet]);

return {
favorites,
favorites: favoritesArray,
loading,
isFavorite,
toggleFavorite,
Expand Down
2 changes: 0 additions & 2 deletions src/pages/CrazyRecipeDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Label } from '@/components/ui/label';
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import { useAuth } from '@/hooks/useAuth';
import { useAdmin } from '@/hooks/useAdmin';
import { useToast } from '@/hooks/use-toast';
import { useRecipeService } from '@/hooks/useRecipeService';
import type { CrazyRecipe, RecipeReview } from '@/services/recipeService';
Expand All @@ -24,7 +23,6 @@ import {
const CrazyRecipeDetail = () => {
const { id } = useParams<{ id: string }>();
const { user } = useAuth();
const { isAdmin } = useAdmin();
const { toast } = useToast();
const navigate = useNavigate();
const { getRecipeById, deleteRecipe, incrementViews: serviceIncrementViews, getRecipeReviews, submitReview } = useRecipeService();
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Favorites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const Favorites = () => {
const navigate = useNavigate();

// Get favorite recipes
const favoriteRecipes = recipes.filter(recipe => favorites.includes(recipe.id));
const favoriteRecipes = recipes.filter(recipe => isFavorite(recipe.id));

// Filter recipes based on search and filters
const filteredRecipes = favoriteRecipes.filter(recipe => {
Expand Down
Loading