在我用Next.js开发的Airbnb克隆中,我无法将Listing添加到收藏夹

cnwbcb6i  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(71)

我的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)但我无法解决它。我已经尝试了各种解决方案,但它们都不起作用。我还是新手,所以请理解。

koaltpgm

koaltpgm1#

基本上,把所有的东西从“favoriteIds”改成“favorites”。这对我很有效。
useFavorite.ts hooks:(app/hooks/useFavorite.ts)

const hasFavorited = useMemo(() => {
    const list = currentUser?.favorites || [];
    // list. Push(listingId);
    return list. Includes(listingId);

}, [currentUser, listingId]);

字符串
route.ts(app/API/favorites/[listingId]/route.ts):

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 favorites = [...(currentUser.favorites || [])];

  favorites.push(listingId);

  const user = prisma.user.update({
    where: {
      id: currentUser.id,
    },
    data: {
      favorites,
    },
  });

  return NextResponse.json(user);
}


对函数执行相同的操作

相关问题