Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[선물 등록, 선물방 편집] 리액트 쿼리 관련 데이터 실시간 업데이트 문제 해결 #339

Merged
merged 17 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
68 changes: 20 additions & 48 deletions src/components/GiftAdd/AddGiftFooter/AddGiftFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
// import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
// import { IcLeft } from '../../../assets/svg';
import usePostGift from '../../../hooks/queries/gift/usePostGift';
import GiftAddNextBtn from '../AddGiftLink/common/GiftAddNextBtn/GiftAddNextBtn';
import * as S from './AddGiftFooter.styled';
import { OpenGraphResponseType } from '../../../types/etc';

interface ItemInfoType {
roomId: number;
}
Expand Down Expand Up @@ -41,68 +37,44 @@ const AddGiftFooter = ({
// fetchPresignedUrl,
// setImageUrl,
}: AddGiftFooterProps) => {
// const updatedItemInfo = {
// roomId: itemInfo.roomId,
// name: name,
// cost: cost,
// imageUrl: imageUrl,
// url: link,
// };
const navigate = useNavigate();
const { mutation } = usePostGift(itemInfo.roomId, targetDate);
const { mutation } = usePostGift(itemInfo.roomId, targetDate, setStep);

const onClick = async () => {
function onClickBtn() {
//빌드 에러 해결용 콘솔
console.log(fileName);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

console.log 부분도 해결이 완료 됐다면 한번 싹 지우는것도 좋을 거 같아요 !!! 콘솔은 이 부분만 코리 남기겠습니다 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리마인드 감사합니다~!!

// const { presignedUrl } = await fetchPresignedUrl(fileName);
// await saveImageUrl(presignedUrl);
// setImageUrl(presignedUrl);
if (isActivated) {
console.log('값', imageUrl);
if (openGraph.image) {
mutation.mutate(
{
roomId: itemInfo.roomId,
name: name,
cost: cost,
imageUrl: imageUrl,
url: link,
},
{
onSuccess: () => {
// console.log('PUT 서버통신 후 presignedUrl', presignedUrl);
navigate(`/add-gift/${itemInfo.roomId}/${targetDate}`);
setStep(0);
},
},
);
mutation.mutate({
roomId: itemInfo.roomId,
name: name,
cost: cost,
imageUrl: imageUrl,
url: link,
});
Comment on lines +49 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

흠.. 뭔가 이 객체를 따로 관리 할 수 있는 방법이 없을까요 ??? 상수화 하여 가독성이 좋아지도록이요!! 이렇게 직접적으로 드러내는게 더 좋은 것 같다면 그것도 오케이인데, if 부분과 else 부분 모두 이 코드가 중복 되어지는거 같아서요!!

Copy link
Contributor Author

@urjimyu urjimyu Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조건문의 경우 presignedUrl을 막으면서 임시로 추가된 것이어서 다른 이슈 해결하면서 중복 제거했습니다! 말씀해주신 대로 상수화를 통해 가독성을 개선하면 훨씬 좋을 것 같네요 감사합니다🥰 context 브랜치에서 상수화 가능할 것 같아서 다른 PR에서 적용해보겠습니다:)

} else {
mutation.mutate(
{
roomId: itemInfo.roomId,
name: name,
cost: cost,
imageUrl: '',
url: link,
},
{
onSuccess: () => {
// console.log('PUT 서버통신 후 presignedUrl', presignedUrl);
navigate(`/add-gift/${itemInfo.roomId}/${targetDate}`);
setStep(0);
},
},
);
mutation.mutate({
roomId: itemInfo.roomId,
name: name,
cost: cost,
imageUrl: '',
url: link,
});
}
}
};
}

// const handlePrevBtnClick = () => {
// setStep(1);
// };

return (
<S.AddGiftFooterWrapper>
<GiftAddNextBtn isActivated={isActivated} onClick={onClick} children='완료' />
<GiftAddNextBtn isActivated={isActivated} onClick={onClickBtn} children='완료' />
</S.AddGiftFooterWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as S from './GiftAddNextBtn.styled';
interface GiftAddNextBtnProps {
children: React.ReactNode;
isActivated: boolean;
onClick: () => void;
onClick: VoidFunction;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오오 !!!! 매우매우 굳입니다 ㅎㅎㅎ 이거 그때 합숙때 알게 된 것이군요 !! 저도 반영해야겠어요😊

}
const GiftAddNextBtn = ({ children, isActivated, onClick }: GiftAddNextBtnProps) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ interface GiftAddPageBottomProps {
}

const GiftAddPageBottom = ({ adPrice, myGiftData }: GiftAddPageBottomProps) => {
const [isAdVisible, setIsAdVisible] = useState(myGiftData.length <= 1);
// const [isAdVisible, setIsAdVisible] = useState(myGiftData.length <= 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석 부분 삭제해도 좋을 거 같아요 ! 😊

const [isAdVisible, setIsAdVisible] = useState(false);

const handleIcCancelClick = () => {
setIsAdVisible(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import GiftAddPageLayoutHeader from './GiftAddPageLayoutHeader';
import useGetMyGift from '../../../hooks/queries/gift/useGetMyGift';
import EmptyGiftAddButtonsWrapper from '../GiftAddButtons/EmptyGiftAddButtonsWrapper';
import useDeleteMyGift from '../../../hooks/queries/gift/useDeleteMyGift';
// import { useEffect } from 'react';

interface GiftAddPageLayoutProps {
roomId: string;
Expand All @@ -19,14 +18,13 @@ interface GiftAddPageLayoutProps {

const GiftAddPageLayout = ({
targetDate,
// step,
roomId,
setStep,
// itemNum,
setItemNum,
}: GiftAddPageLayoutProps) => {
const roomIdNumber = parseInt(roomId);
const { data } = useGetMyGift({ roomId: roomIdNumber });
const { data } = useGetMyGift({ roomId: roomIdNumber});
setItemNum(data.data.myGiftDtoList.length);
const parsedRoomId = parseInt(roomId);
const { mutation } = useDeleteMyGift(parsedRoomId);
Expand All @@ -42,10 +40,6 @@ const GiftAddPageLayout = ({
mutation.mutate(giftId);
};

// useEffect(()=> {

// }, [])

return (
<S.GiftAddPageWrapper>
<GiftAddPageLayoutHeader title={'내가 등록한 선물'} />
Expand Down
9 changes: 4 additions & 5 deletions src/hooks/queries/gift/useDeleteMyGift.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { del } from '../../../apis/client';
import { MY_GIFT_QUERY_KEY } from './useGetMyGift';

export const deleteMyGift = async (giftId: number) => {
export async function deleteMyGift(giftId: number) {
await del(`/gift/my/${giftId}`);
};
}

export const useDeleteMyGift = (roomId: number) => {
export function useDeleteMyGift(roomId: number) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

화살표 함수가 아닌 선언식의 형태로 함수를 작성한 이유가 궁금합니다 !!
초반 컨벤션을 정할때 화살표 함수로 지정했던 거 같아서요 !! react-query에선 특별한 이유가 있나요 ~??
다른 부분에도 모두 동일하게 변동 되어 있길래, 해당 부분에만 리뷰 남깁니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것은 그저 저의 착각으로 인한 실수입니다!ㅠㅠ 작업하다가 갑자기 컨벤션 착각해서 막 고쳤는데 알려주셔서 감사합니다✨

const queryClient = useQueryClient();

const mutation = useMutation({
mutationKey: [MY_GIFT_QUERY_KEY[0]],
mutationFn: deleteMyGift,
onSuccess() {
console.log('선물 삭제 성공');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mutationKey 제거 대박 너무 좋아요!
제가 최근에 강의를 봤는데 사용자에게 변경사항을 실제로 보여줄 땐 3가지 방법이 있다합니다!

  1. 낙관적인 업데이트 : 서버 호출이 잘될 거라 가정하고, 잘 안됐을 경우 되돌리는 방식
  2. 서버에서 받은 데이터를 가져오는 겁니다 : mutation을 실행 할 때 업데이트 된 데이터를 가져와 리액트 쿼리 캐시를 업데이트하는 방식
  3. 마지막으로 관련 쿼리를 무효화하는 방식 : 쿼리를 무효화하면, 클라 데이터를 서버의 데이터와 동기화하기 위해 서버에 재요청이 발생하는 방식
스크린샷 2024-02-14 오후 4 20 44 그래서 언니 방식이 3번째 방식으로 진행되었구나! 바로 이해가 쏘옥 되었습니다. 혹시 다른 분들을 위해 리뷰 남겨봅니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

대박.. 이 리뷰 너무너무 좋아요 이렇게 3가지 방식이 있고, 저희는 3번째 방식으로 진행하고 있다는 사실이 이해가 쏙쏙 된거 같습니다 !!!!! 너무너무 감사해요 너무너무 좋아요 !!!!!!!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와,, 정말 리액트 쿼리 설명 너무 이해 쏙쏙 되게 깊이 있게 해주셔서 감사해요!! 덕분에 더 깊은 내용까지 이해하게 되었습니다🫶 좋은 레퍼런스까지,, 정말 감사해요!

Expand All @@ -22,6 +21,6 @@ export const useDeleteMyGift = (roomId: number) => {
});

return { mutation };
};
}

export default useDeleteMyGift;
28 changes: 18 additions & 10 deletions src/hooks/queries/gift/usePostGift.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { post } from '../../../apis/client';
import { GiftPostRequestType } from '../../../types/gift';
import { useNavigate } from 'react-router-dom';
import { MY_GIFT_QUERY_KEY } from './useGetMyGift';

export const postNewGift = async (body: GiftPostRequestType) => {
post(`/gift`, body);
};
export async function postNewGift(body: GiftPostRequestType) {
await post(`/gift`, body);
}

export const usePostGift = (roomId: number, targetDate: string) => {
export function usePostGift(
roomId: number,
targetDate: string,
setStep: React.Dispatch<React.SetStateAction<number>>,
) {
const navigate = useNavigate();

const queryClient = useQueryClient();

const mutation = useMutation({
mutationFn: postNewGift,
onSuccess: () => {
console.log('roomId, targetDate', roomId, targetDate);
console.log('선물 등록 성공!!');
queryClient.invalidateQueries({ queryKey: [MY_GIFT_QUERY_KEY[0], roomId] });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아! 혹시 쿼리클라이언트의 setQueryData 가 있던데 invalidateQuries를 사용한 이유가 궁금해서 또 찾아보니
setQueryData : 낙관적 업데이트(위에서 언급한 1번)에 가장 적합하거나 서버 상태에 영향을 주지 않고 일시적으로 캐시를 재정의해야 하는 경우에 가장 적합하다 하네요!
invalidateQueries : 변형이나 기타 중요한 변경 후 클라이언트 캐시가 서버의 현재 상태를 반영하도록 하는 데 더 적합하다하여 아항 이럴경우 invalidateQureis를 사용해야함을 깨달았습니다..! 네이밍이 뭔가 setQueryData도 될 것 같아서 찾아보았습니다 ! ㅎㅎ
tanstack쿼리 클라이언트 공식문서

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게나 섬세한 리뷰 너무나 소중하네요❣️ setQueryData에 대해서는 제대로 알지 못했는데 말씀해주신 대로 더 적절할 것 같네요! 수정해보겠습니다:)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰해주신 부분 참고해서 POST 관련된 부분들은 setQueryData로 수정했습니다 감사합니다!

Copy link
Contributor Author

@urjimyu urjimyu Feb 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클라이언트 캐시가 선물이 등록된 상태의 서버 상태를 반영해야 하는 상황이라 2번이 더 적합한 것 같더라고요..! setQueryData는 실시간 업데이트가 안 되는 문제 사항을 발견해서 모두 invalidateQueries로 돌려놓았습니다🥲 리액트 쿼리의 데이터 업데이트 방법에 대해 더 공부해보겠습니다!

navigate(`/add-gift/${roomId}/${targetDate}`);
setStep(0);
},

onError: () => {
console.log('선물 등록 에러!!');
onError: (error) => {
console.log('선물 등록 에러!!', error.message);
},
});

return { mutation };
};
}

export default usePostGift;
10 changes: 4 additions & 6 deletions src/hooks/queries/room/useDeleteRoomMember.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@ export const deleteRoomMember = async ({ roomId, memberId }: DeleteRoomMember) =
await del(`/room/${roomId}/members/${memberId}`);
};

const useDeleteRoomMember = ({ roomId, memberId }: DeleteRoomMember) => {
const useDeleteRoomMember = ({ roomId }: DeleteRoomMember) => {
const queryClient = useQueryClient();

const mutation = useMutation({
mutationKey: [ROOM_MEMBER_QUERY_KEY[0]],
mutationFn: deleteRoomMember,
onSuccess() {
console.log('삭제 성공');
queryClient.invalidateQueries({ queryKey: [ROOM_MEMBER_QUERY_KEY[0], roomId, memberId] });
queryClient.invalidateQueries({ queryKey: [...ROOM_MEMBER_QUERY_KEY, roomId] });
},
onError: () => {
console.log('선물 삭제 중 에러가 발생했습니다.');
onError: (error) => {
console.log('선물 삭제 중 에러가 발생했습니다.', error.message);
},
});
return { mutation };
Expand Down
16 changes: 12 additions & 4 deletions src/pages/MyPage/EditRoom/CardGuest/CardGuest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import Type1Tag from '../../../../components/IcTag/Type1/Type1';
import ProfileImage from '../../ProfileImage/ProfileImage';
import * as S from './CardGuest.style';
import useDeleteRoomMember from '../../../../hooks/queries/room/useDeleteRoomMember';
import { useLocation } from 'react-router';
// import { useLocation } from 'react-router';
import { useParams } from 'react-router-dom';

interface CardGuestProps {
user: string;
Expand All @@ -13,9 +14,16 @@ interface CardGuestProps {
}

const CardGuest = ({ user, makerState, profileImageUrl, memberId }: CardGuestProps) => {
const location = useLocation();
const searchParams = new URLSearchParams(location.search);
const roomIdString = searchParams.get('roomId');
// 주소가 path variable 형태이기 때문에 location.search로 쿼리 파라미터를 가져올 수 없어
// 룸아이디가 null로 받아지고 있었고, 이로 인해 400에러가 발생해서 임의로 수정했습니다
// const location = useLocation();
// const searchParams = new URLSearchParams(location.search);

// const roomIdString = searchParams.get('roomId');

const params = useParams();
const roomIdString = params.roomId;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pr 내용 봤습니다 !! : ) 해당 부분 주석 삭제해주셔도 될거 같아요 !! ㅎㅎ

const roomId = parseInt(roomIdString || '');
console.log('추출된 초대 코드', roomId);
const { mutation } = useDeleteRoomMember({ roomId, memberId });
Expand Down
10 changes: 5 additions & 5 deletions src/pages/MyPage/EditRoom/EditRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ const EditRoom = () => {
console.log('추출된 초대 코드', roomId);
const roomWholeMemberData = useGetRoomMember(roomId).data;
const roomWholeOwnerData = useGetRoomOwner(roomId).data;
console.log(roomWholeMemberData);
console.log(roomWholeOwnerData);

const roomOwnerData = roomWholeOwnerData?.owner;
const roomGifteeData = roomWholeOwnerData.room;
const roomMemberData = roomWholeMemberData.members;
const roomMemberData = roomWholeMemberData;

console.log(roomOwnerData);
console.log('개설자 정보', roomOwnerData);
console.log('선물방', roomGifteeData);
console.log('선물 준비 멤버 정보', roomMemberData);

return (
<S.EditRoomWrapper>
Expand All @@ -36,7 +36,7 @@ const EditRoom = () => {
<CardGuest
user={roomOwnerData.name}
makerState={true}
profileImageUrl={roomOwnerData?.profileImgUrl}
profileImageUrl={roomOwnerData?.profileImageUrl}
memberId={roomOwnerData.ownerId}
/>

Expand Down
2 changes: 1 addition & 1 deletion src/types/member/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type RoomMemberType = {

export type OwnerListType = {
ownerId: number;
profileImgUrl: string;
profileImageUrl: string;
name: string;
};

Expand Down