我的Airbnb克隆项目是用Next.js开发的,我无法将Listing添加到收藏夹中。点击时,心形按钮(组件)的颜色没有变化,即使我收到成功响应。添加到收藏夹中的内容没有保存在数据库中。(我使用MongoDB和prisma)hasFavorited变量总是返回false,因为它试图在空数组中查找listingId。
这是我的ListingCard.tsx:(app/components/listings/ListingCard.tsx)
'use client';
import { useCallback, useMemo } from "react";
import useCountries from "@/app/hooks/useCountries";
import { SafeUser } from "@/app/types";
import { Listing, Reservation } from "@prisma/client";
import { useRouter } from "next/navigation";
import { format } from 'date-fns';
import Image from "next/image";
import HeartButton from "../HeartButton";
import Button from "../Button";
interface ListingCardProps {
data: Listing;
reservation?: Reservation;
onAction?: (id: string) => void;
disabled?: boolean;
actionLabel?: string;
actionId?: string;
currentUser?: SafeUser | null;
}
const ListingCard:React.FC<ListingCardProps> = ({
data,
reservation,
onAction,
disabled,
actionId = "",
actionLabel,
currentUser
}) => {
const router = useRouter();
const { getByValue } = useCountries();
const location = getByValue(data.locationValue);
const handleCancel = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
if (disabled) {
return;
}
onAction?.(actionId);
},
[actionId, onAction, disabled]
);
const price = useMemo(() => {
if (reservation) {
return reservation.totalPrice;
}
return data.price;
}, [reservation, data.price]);
const reservationDate = useMemo(() => {
if (!reservation) {
return null;
}
const start = new Date(reservation.startDate);
const end = new Date(reservation.endDate);
return `${format(start,'PP')} - ${format(end,'PP')}`;
}, [reservation]);
return (
<div
onClick={()=>router.push(`/listings/${data.id}`)}
className="col-span-1 cursor-pointer group">
<div className="flex flex-col gap-2 w-full">
<div className="aspect-square w-full relative overflow-hidden rounded-xl">
<Image
alt="Listing"
src={data.imageSrc}
fill
className="object-cover h-full w-full group-hover:scale-110 transition"
/>
<div className="absolute top-3 right-3">
<HeartButton
listingId={data.id}
currentUser = {currentUser}
/>
</div>
</div>
<div className="font-semibold text-lg">
{location?.region}, {location?.label}
</div>
<div className="font-light text-neutral-500">
{reservationDate || data.category}
</div>
<div className="flex flex-row items-center gap-1">
<div className="font-semibold">
$ {price}
</div>
{!reservation && (
<div className="font-light">night</div>
)}
</div>
{onAction && actionLabel && (
<Button
disabled={disabled}
small
label={actionLabel}
onClick={handleCancel}
/>
)}
</div>
</div>
)
}
export default ListingCard
字符串
这是我的useFavorite.ts hooks:(app/hooks/useFavorite.ts)
import axios from "axios";
import { useRouter } from "next/navigation";
import { useCallback, useMemo } from "react";
import { toast } from "react-hot-toast";
import { SafeUser } from "../types";
import useLoginModal from "./useLoginModal";
interface IUseFavorite {
listingId: string;
currentUser?: SafeUser | null;
}
const useFavorite = ({ listingId, currentUser }: IUseFavorite) => {
const router = useRouter();
const loginModal = useLoginModal();
const hasFavorited = useMemo(() => {
// console.log(currentUser?.favoriteIds);
// console.log(listingId);
const list = currentUser?.favoriteIds || [];
// list.push(listingId);
return list.includes(listingId);
}, [currentUser, listingId]);
const toggleFavorite = useCallback(async (e:React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
if(!currentUser) {
// login modal will open if tries to add to favorites without logging in.
return loginModal.onOpen();
}
// console.log(currentUser);
try {
let request;
if (hasFavorited) {
request = () => axios.delete(`/api/favorites/${listingId}`);
} else {
request = () => axios.post(`/api/favorites/${listingId}`);
}
await request();
router.refresh();
toast.success('Success');
} catch (error) {
toast.error('Something went wrong');
}
}, [currentUser, hasFavorited, listingId, loginModal, router]);
// console.log(hasFavorited);
return {
hasFavorited,
toggleFavorite
}
}
export default useFavorite;
型
下面是我的route.ts(app/API/favorites/[listingId]/route.ts):
import { NextResponse } from "next/server";
import getCurrentUser from "@/app/actions/getCurrentUser";
import prisma from "@/app/libs/prismadb";
interface IParams {
listingId?: string;
}
export async function POST(request: Request, { params }: { params:IParams } ) {
const currentUser = await getCurrentUser();
if (!currentUser) {
return NextResponse.error();
}
const { listingId } = params;
// console.log("route listing id: " + listingId);
if (!listingId || typeof listingId !== 'string') {
throw new Error('Invalid ID');
}
let favoriteIds = [...(currentUser.favoriteIds || [])];
favoriteIds.push(listingId);
const user = prisma.user.update({
where: {
id: currentUser.id
},
data: {
favoriteIds: favoriteIds
}
});
return NextResponse.json(user);
}
export async function DELETE(request:Request, { params }: { params: IParams }) {
const currentUser = await getCurrentUser();
if (!currentUser) {
return NextResponse.error();
}
const { listingId } = params;
if (!listingId || typeof listingId !== 'string') {
throw new Error('Invalid ID');
}
let favoriteIds = [...(currentUser.favoriteIds || [])];
favoriteIds = favoriteIds.filter((id) => id !== listingId);
// the selected id out of array
const user = await prisma.user.update({
where: {
id: currentUser.id
},
data: {
favoriteIds: favoriteIds
}
});
return NextResponse.json(user);
}
型
这是我的HeartButton.tsx组件(app/components/HeartButton.tsx):
'use client';
import { AiFillHeart, AiOutlineHeart } from "react-icons/ai";
import { SafeUser } from "../types";
import useFavorite from "../hooks/useFavorite";
import ClientOnly from "./ClientOnly";
interface HeartButtonProps {
listingId: string;
currentUser?: SafeUser | null
}
const HeartButton: React.FC<HeartButtonProps> = ({
listingId,
currentUser
}) => {
// we are gonna modified later.
// const hasFavorited = false;
// const toggleFavorite = () => {};
// replaced it with useFavorite hook
// console.log(listingId);
const { hasFavorited, toggleFavorite } = useFavorite({listingId, currentUser});
// console.log(hasFavorited);
return (
<div onClick={toggleFavorite}
className="relative hover:opacity-80 transition cursor-pointer">
<AiOutlineHeart
size={28}
className="fill-white absolute -top-[2px] -right-[2px]"
/>
<AiFillHeart
size={24}
className={
hasFavorited ? "fill-rose-600" : "fill-neutral-500/70"
}
/>
</div>
)
}
export default HeartButton
型
我知道这可能是一个简单的错误,(useFavorited总是返回false)但我无法解决它。我已经尝试了各种解决方案,但它们都不起作用。我还是新手,所以请理解。
1条答案
按热度按时间koaltpgm1#
基本上,把所有的东西从“favoriteIds”改成“favorites”。这对我很有效。
useFavorite.ts hooks:(app/hooks/useFavorite.ts)
字符串
route.ts(app/API/favorites/[listingId]/route.ts):
型
对函数执行相同的操作