게시물 좋아요 기능..

먼지·2022년 9월 29일
2

TIL

목록 보기
34/57
post-thumbnail

Post page component에서 게시글 하나 데이터를 리액트 쿼리로 가져오는데 좋아요를 누른 사람의 배열 likeList는 { userId(userkey): number, likeId: number, nickname: string, username(userid): string } 객체로 이루어져 있다.

likePost api 요청이 성공적으로 완료되면 위와 같은 객체가 추가된 likeList 배열이 오는데, 좋아요하거나 취소할 때마다 다시 게시글 데이터를 불러오면 비효율적?이라고 생각했다.

useState

그래서 처음 생각한 방법은 isLiked란 react state를 만들어서 data?.likeList.find(...) 로 현재 유저가 좋아요한 정보 객체를 저장해서 객체가 존재한다면 좋아요된 상태라는 fillHeart icon을 표시했는데, 여기서 문제는 처음에 접속했을 때는 state 안에 데이터가 잘 담겨있는데 새로고침을 하면 isLike가 undefined로 나온다ㅜㅜ

export default function Post() {
  const navigate = useNavigate();
  const { state } = useLocation();
  const { id } = useParams<{ id: string }>();
  const { data } = useQuery(`BoardDetail${id || state}`, () =>
    getPostRequest(Number(id) || Number(state))
  );
  console.log('Post data', data);
  const currentUser = useRecoilValue(currentUserState);
  const [isLiked, setIsLiked] = useState(
    data?.likeList.some(({ userId }) => userId === currentUser?.userid)
  ); // undefined
  ...

왜냐면 api는 비동기고 보통 리액트에선 useEffect로 마운트 후에 데이터를 fetching해서 state를 변경하는데 나는 react-query를 쓰니 코드를 한 줄 한 줄 읽었을 때 게시글 데이터를 가져오지 않은 상태면 isLiked state가 likeList 값을 읽지 못하는 것 같다.?

Query Invalidation

export default function Post() {
  ...
  
  // app에 선언한 거 export 할수있나?
  const queryClient = useQueryClient();

  const handleLikePost = () => {
    if (!currentUser?.accessToken) {...}
    likePostRequest(currentUser.accessToken, Number(id))
      .then((res) => {
        ...
        queryClient.invalidateQueries([`BoardDetail${id || state}`]);
      })
      .catch((err) => {
      })

브라우저에 포커스 될 때도 호출되니 조회 수가 2갸씩 증가함..

난 바보다. 리액트 쿼리를 쓰면서 useState를 또 사용할 생각을 하다니; 지난번에도 이거랑 똑같은 문제를 겪었는데 정리를 안 해놓으니 완전 잊고 있엇다....!!

React Query useMutation

참고
https://maxkim-j.github.io/posts/react-query-preview
https://tanstack.com/query/v4/docs/reference/useMutation

리액트 쿼리에선 데이터를 조회할 때는 useQuery, 서버에 create, update, delete 등 데이터 변경 작업을 요청할 때나 server state에 사이드 이펙트를 일으킬 때 useMutation을 사용한다. 반환하는 객체 프로퍼티 상태값은 useQuery와 동일

const {
  data,
  error,
  isError,
  isIdle,
  isLoading,
  isPaused,
  isSuccess,
  mutate,
  mutateAsync,
  reset,
  status,
} = useMutation(mutationFn, {
  cacheTime,
  mutationKey,
  networkMode,
  onError,
  onMutate,
  onSettled,
  onSuccess,
  retry,
  retryDelay,
  useErrorBoundary,
  meta
})

mutate

useMutation을 정의 해둔 뒤 이벤트가 발생되었을 때 mutate를 사용. variables는 mutationFn에 전달하는 객체?

mutate(variables, {
  onError, // 에러를 만나면 절달됨
  onSettled, // 성공한 데이터나 에러가 전달될 때 실행
  onSuccess, // mutation 성공 후 결과를 전달할 때 실행됨
})

공부 후 정리

구현

참고해야지ㅣ
https://abangpa1ace.tistory.com/266
https://react-query-v3.tanstack.com/guides/mutations

지난번에 작성한 코드를 참고해서 작성했는데 일단 성공

export default function Post() {
  ...
  
  const queryClient = useQueryClient();
  
  const { mutate: likePost } = useMutation(
    () => likePostRequest(currentUser?.accessToken as string, Number(id)),
    {
      onSuccess: (data, variables, context) => {
        console.log('likePost success!', data, variables, context);
        queryClient.setQueryData(['Post', id], (old: any) => ({
          ...old,
          likeList: data,
        }));
      },
      onError: (err, variables, context) => {
        console.log('likePost success', err, variables, context);
      },
    }
  );

  const { mutate: cancleLikePost } = useMutation(
    (likeId: number) =>
      cancelLikePostRequest(currentUser?.accessToken as string, likeId),
    {
      onSuccess: (data, variables, context) => {
        console.log('cancleLikePost success!', data, variables, context);
        queryClient.setQueryData(['Post', id], (old: any) => ({
          ...old,
          likeList: data,
        }));
      },
      onError: (err, variables, context) => {
        console.log('cancleLikePost success', err, variables, context);
      },
    }
  );

  const handleLikeOrCancelLikePost = () => {
    if (!currentUser?.accessToken) {
      if (confirm('로그인이 필요한 서비스 입니다. 로그인 하시겠습니까?!')) {
        navigate('/register', { state: { path: `/board/${id}` } });
        return;
      }
      return;
    }
    // const liked = new Set(data?.likeList.map(({ userId }) => userId)).has(
    //   currentUser?.userid
    // );
    const liked = data?.likeList.find(
      ({ userId }) => userId === currentUser.userid
    );
    liked ? cancleLikePost(Number(liked.likeId)) : likePost();
  };

마무리

React Query를 두 번째 사용하는데 처음 사용했을 때 커뮤니티를 만들었어서 꽤나 열심히 공부했었다. 근데 지금도 글치만 만드는 것도 지치니 정리할 생각을 못 해서 거의 잊고 있었는데 한 번 사용해 봤으니 머 전보단 낫겠지^ 이 생각으로 리액트 쿼리를 도입했는데 워낙 상태 관리하기 좋기도 하고! 그랬는데 새로 배우는 것 같다ㅜ

블로그에 글 쓰는 건 귀찮은 것도 크지만,, 글을 잘 정리해서 못 쓰고 뭔가 잘못된 정보를 올릴 수도 있고 내가 배운 게 별거 아닐 거라 생각해서 올리는 게 창피해서 꺼렸는데 이제는 기억하고 싶은 중요한 내용을 올리는 것도 좋겠지만 그날그날 배운 거를 완벽하지 않더라도 조금이라도 적어놔야겠다고 느꼈다.

profile
꾸준히 자유롭게 즐겁게

0개의 댓글