24/1/26 좋아요 기능 구현
2024. 1. 29. 08:34ㆍ카테고리 없음
심화플러스 주차에 만들었던 좋아요 기능을 리뷰 해보자
<LikeBtn.jsx>
import React from "react";
import styled from "styled-components";
import HeartEmpty from "../../assets/images/HeartEmpty.png"; //좋아요가 눌린상태인지 여부에
import HeartFull from "../../assets/images/HeartFull.png"; //따른 이미지 두개
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; //CUD를 위한 useMutation
import { addHeart, deleteHeart, getHeart } from "./queryFns"; //과 Read를 위한 useQuery
import { useSelector } from "react-redux"; //쿼리펑션들은 따로 보관했다.
import Swal from "sweetalert2";
export default function LikeBtn({ name, id }) {
const { isLogin, uid /* email, displayName, photoURL */ } = useSelector(
(state) => state.authSlice //유저정보를 담은 툴킷의 스테이트를 유즈셀렉터로 불러온다.
);
// useEffect(() => {
// console.log(user, "이거 유저다");
// }, []);
// const [like, setLIke] = useState(false);
const { data: hearts, isLoading } = useQuery({ //유즈 쿼리 사용시 소괄호안에는 객체가 들어오고
queryKey: ["hearts"], //필수 요소인 쿼리키와 쿼리펑션이 있다.
queryFn: getHeart, //쿼리펑션들을 담은 파일들은 별도 보관했고 밑에서 설명예정
});
// console.log(posts, " 데이터야2");
const queryClient = useQueryClient(); //유즈 뮤테이션을 사용하기 위한 쿼리클라이언트
const { mutate: mutateToAdd } = useMutation({ //이것을 설정해야 유즈쿼리의 캐시데이터에 접근가능
mutationFn: addHeart, //하트추가 함수 쿼리펑션파일에 보관중
// onSuccess: async () => {
// await queryClient.invalidateQueries({ queryKey: ["hearts"] }); //주석처리된부분은
// }, //일반적인 로직
onMutate: async (newHeart) => {
await queryClient.cancelQueries({ queryKey: ["hearts"] }); //좋아요의 디스플레이에 즉각적인
//변화를 위한 옵티미스틱 업데이트로직 const previousHearts = queryClient.getQueryData(["hearts"]); //일단 현재 캐쉬데이터를 취소
//쿼리데이터를 이전데이터로 명명 queryClient.setQueryData(["hearts"], (old) => [...old, newHeart]); //그리고 새로운 하트를 추가해서
//캐시데이터로써 세팅 return { previousHearts }; //이전데이터는 남긴다.
},
onError: (err, newHeart, context) => {
queryClient.setQueryData(["hearts"], context.previousHearts); //비동기통신에 에러발생시
}, //저장해놨던 예전데이터로 롤백
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["hearts"] }); //에러없이 정상적 통신이 완료됐으면
}, //onMutate때 세팅했던 값으로 정착
});
const { mutate: mutateToDelete } = useMutation({ //좋아요 삭제로직
mutationFn: deleteHeart, //요함수도 밑에서 다룸
// onSuccess: async () => {
// await queryClient.invalidateQueries({ queryKey: ["hearts"] });
// },
onMutate: async (newHeart) => {
await queryClient.cancelQueries({ queryKey: ["hearts"] });
const previousHearts = queryClient.getQueryData(["hearts"]);
queryClient.setQueryData(["hearts"], (old) => [...old, newHeart]);
return { previousHearts };
},
onError: (err, newHeart, context) => {
queryClient.setQueryData(["hearts"], context.previousHearts);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["hearts"] });
},
});
// let userId_fake;
// useEffect(() => {
// userId_fake = nanoid();
// }, []);
const filteredHearts = hearts?.filter((heart) => { //유즈쿼리의 하트들을 로그 인한 유저와
return heart.gameId === id; //아이디가 같은애의 배열을 구한다.
});
const filterdHeart1 = hearts?.find((heart) => { //보여질페이지의 게임에 아이디와
return heart.gameId === id && heart.uid === uid; //유저아이디가 같은 배열안의 요소 한개 구한다.
});
// console.log(filterdHeart1, "이거 하트들");
const selectedId = filterdHeart1?.id; //
const likeBTN = () => {
if (!isLogin) return Swal.fire("로그인 후에 이용이 가능합니다."); //로그인한사람만 누를수 있다.
// setLIke(!like);
if (filterdHeart1) { //이미 좋아요를 누른사람은 좋아요가 삭제되고
mutateToDelete(selectedId); //selectedId를 인자로 전달한다.
} else {
mutateToAdd({ uid, id }); //안누르는 사람은 추가되는 로직
} //매개변수로 uid와 id를 넘겨준다. 좋아요가 어떤게임에
}; //어떤유저에 관한 것인지 얻기위한 정보
if (isLoading) {
return <>로딩중...</>;
}
return (
<>
{/* <LikeButton $like={like.toString()} onClick={likeBTN}>
좋아요 버튼
</LikeButton> */}
<ImageCount>
<ImageWrapper name={name} onClick={likeBTN}>
{filterdHeart1 ? (
<img id="이미지" name={name} src={HeartFull} alt="꽉찬하트"></img>
) : (
<img id="이미지" name={name} src={HeartEmpty} alt="빈하트"></img>
)}
</ImageWrapper>
<StP f name={name}>
{filteredHearts?.length}
</StP>
</ImageCount>
</>
);
}
// const LikeButton = styled.div`
// cursor: pointer;
// ${(props) => {
// switch (props.$like) {
// case "true":
// return css`
// background-color: red;
// `;
// default:
// return css`
// background-color: white;
// `;
// }
// }}
// `;
const ImageCount = styled.div`
display: flex;
align-items: center;
justify-content: flex-end;
width: 60%;
gap: 10px;
`;
const ImageWrapper = styled.div`
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
overflow: hidden;
& img {
width: 100%;
height: 100%;
}
`;
const StP = styled.p`
color: var(--red);
font-size: 20px;
text-align: center;
`;
// 좋아요 버튼 연타시 에러가 나거나 좋아요가 연속 두번 눌리는등의 버그 발생
// 이후 프로젝트에서 좋아요 기능 구현시 useState에 true false 두개로 변화하는 토글 로직을 만들고
//onMutate시작할때 useState의 상태를 false로 바꾸어 버튼을 비활성화 시키고 onSettled의 마지막에
//useState의 상태를 true로 만들어 버튼을 누를수 있게 만듦으로써 해결했다.
<queryFn.js> 쿼리 펑션들을 담아놓은 파일이다.
import {
collection, //데이터베이스는 firebase를 사용했다.
getDocs,
doc,
addDoc,
deleteDoc,
} from "@firebase/firestore";
import { db } from "../../shared/firebase";
export const getHeart = async () => {
let data = [];
const response = await getDocs(collection(db, "hearts")); //모든 좋아요들을 불러오는 로직
// console.log(response.docs, "리스판스야");
// data = response.docs;
response.forEach((doc) => {
const docData = doc.data();
// console.log(zzz, "크크크야");
data.push({ ...docData, id: doc.id });
});
// console.log(data, " 데이터야");
return data;
};
export const addHeart = async ({ uid, id }) => { //좋아요를 추가하는 로직 매개변수로 uid와 게임아이디
await addDoc(collection(db, "hearts"), { //인 id를 받는다.
uid,
gameId: id,
haha: "haha", //하하는 내 취향 ㅋㅋ
});
};
export const deleteHeart = async (selectedId) => { //지우기위해서 특정해야하는 아이디를 가져온다.
await deleteDoc(doc(db, "hearts", `${selectedId}`));
};
// 요 당시에 좋아요 기능은 정석적으로 하기보다. 기본적으로 내 아이디어의 기반해서 구현해본것이기
//때문에 뭔가 어설플수 있다. 조언 해주세요