diff --git a/frontend/src/features/comments/components/CommentForm.tsx b/frontend/src/features/comments/components/CommentForm.tsx index 3fda9f16..8f1dbca7 100644 --- a/frontend/src/features/comments/components/CommentForm.tsx +++ b/frontend/src/features/comments/components/CommentForm.tsx @@ -1,3 +1,4 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; import { css, styled } from 'styled-components'; import defaultAvatar from '@/assets/icon/avatar-default.svg'; @@ -7,7 +8,7 @@ import LoginModal from '@/features/auth/components/LoginModal'; import Avatar from '@/shared/components/Avatar'; import useToastContext from '@/shared/components/Toast/hooks/useToastContext'; import { useOverlay } from '@/shared/hooks/useOverlay'; -import { usePostCommentMutation } from '../queries'; +import { postComment } from '../remotes/comments'; interface CommentFormProps { songId: number; @@ -18,6 +19,7 @@ const CommentForm = ({ songId, partId }: CommentFormProps) => { const [newComment, setNewComment] = useState(''); const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const overlay = useOverlay(); + const queryClient = useQueryClient(); const openLoginModal = () => { setIsLoginModalOpen(true); @@ -38,10 +40,12 @@ const CommentForm = ({ songId, partId }: CommentFormProps) => { const isLoggedIn = !!user; - const { - postNewComment, - mutations: { isPending: isPendingPostComment }, - } = usePostCommentMutation(); + const { mutate: postNewComment, isPending: isPendingPostComment } = useMutation({ + mutationFn: postComment, + onSuccess: (_, { songId, partId }) => { + queryClient.invalidateQueries({ queryKey: ['comments', songId, partId] }); + }, + }); const { showToast } = useToastContext(); diff --git a/frontend/src/features/comments/components/CommentList.tsx b/frontend/src/features/comments/components/CommentList.tsx index 741ac7b8..a6170675 100644 --- a/frontend/src/features/comments/components/CommentList.tsx +++ b/frontend/src/features/comments/components/CommentList.tsx @@ -1,10 +1,11 @@ +import { useQuery } from '@tanstack/react-query'; import { styled } from 'styled-components'; import cancelIcon from '@/assets/icon/cancel.svg'; import BottomSheet from '@/shared/components/BottomSheet/BottomSheet'; import Spacing from '@/shared/components/Spacing'; import SRHeading from '@/shared/components/SRHeading'; import { useOverlay } from '@/shared/hooks/useOverlay'; -import { useCommentsQuery } from '../queries'; +import { commentsQueryOptions } from '../queries'; import Comment from './Comment'; import CommentForm from './CommentForm'; import type { Comment as CommentType } from '../types/comment.type'; @@ -41,7 +42,7 @@ const CommentList = ({ songId, partId }: CommentListProps) => { )); - const { comments } = useCommentsQuery(songId, partId); + const { data: comments } = useQuery(commentsQueryOptions(songId, partId)); if (!comments) { return null; diff --git a/frontend/src/features/comments/queries/index.ts b/frontend/src/features/comments/queries/index.ts index 1e52cb10..a8125850 100644 --- a/frontend/src/features/comments/queries/index.ts +++ b/frontend/src/features/comments/queries/index.ts @@ -1,24 +1,8 @@ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { getComments, postComment } from '../remotes/comments'; +import { queryOptions } from '@tanstack/react-query'; +import { getComments } from '../remotes/comments'; -export const useCommentsQuery = (songId: number, partId: number) => { - const { data: comments, ...queries } = useQuery({ +export const commentsQueryOptions = (songId: number, partId: number) => + queryOptions({ queryKey: ['comments', songId, partId], queryFn: () => getComments(songId, partId), }); - - return { comments, queries }; -}; - -export const usePostCommentMutation = () => { - const client = useQueryClient(); - - const { mutate: postNewComment, ...mutations } = useMutation({ - mutationFn: postComment, - onSuccess: (_, { songId, partId }) => { - client.invalidateQueries({ queryKey: ['comments', songId, partId] }); - }, - }); - - return { postNewComment, mutations }; -}; diff --git a/frontend/src/features/songs/hooks/useExtraSongDetail.ts b/frontend/src/features/songs/hooks/useExtraSongDetail.ts index cbabaf23..d1556732 100644 --- a/frontend/src/features/songs/hooks/useExtraSongDetail.ts +++ b/frontend/src/features/songs/hooks/useExtraSongDetail.ts @@ -1,9 +1,10 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; import { useCallback, useRef } from 'react'; import useValidParams from '@/shared/hooks/useValidParams'; import createObserver from '@/shared/utils/createObserver'; import { - useExtraNextSongDetailsInfiniteQuery, - useExtraPrevSongDetailsInfiniteQuery, + extraPrevSongDetailsInfiniteQueryOptions, + extraNextSongDetailsInfiniteQueryOptions, } from '../queries'; import type { Genre } from '../types/Song.type'; @@ -11,16 +12,22 @@ const useExtraSongDetail = () => { const { id: songIdParams, genre: genreParams } = useValidParams(); const { - extraPrevSongDetails, - fetchExtraPrevSongDetails, - infiniteQueries: { isLoading: isLoadingPrevSongDetails, hasPreviousPage }, - } = useExtraPrevSongDetailsInfiniteQuery(Number(songIdParams), genreParams as Genre); + data: extraPrevSongDetails, + fetchPreviousPage: fetchExtraPrevSongDetails, + isLoading: isLoadingPrevSongDetails, + hasPreviousPage, + } = useInfiniteQuery( + extraPrevSongDetailsInfiniteQueryOptions(Number(songIdParams), genreParams as Genre) + ); const { - extraNextSongDetails, - fetchExtraNextSongDetails, - infiniteQueries: { isLoading: isLoadingNextSongDetails, hasNextPage }, - } = useExtraNextSongDetailsInfiniteQuery(Number(songIdParams), genreParams as Genre); + data: extraNextSongDetails, + fetchPreviousPage: fetchExtraNextSongDetails, + isLoading: isLoadingNextSongDetails, + hasNextPage, + } = useInfiniteQuery( + extraNextSongDetailsInfiniteQueryOptions(Number(songIdParams), genreParams as Genre) + ); const prevObserverRef = useRef(null); const nextObserverRef = useRef(null); diff --git a/frontend/src/features/songs/hooks/useSongDetailEntries.ts b/frontend/src/features/songs/hooks/useSongDetailEntries.ts index ca0cba30..d9e18771 100644 --- a/frontend/src/features/songs/hooks/useSongDetailEntries.ts +++ b/frontend/src/features/songs/hooks/useSongDetailEntries.ts @@ -1,15 +1,15 @@ +import { useQuery } from '@tanstack/react-query'; import { useCallback } from 'react'; import useValidParams from '@/shared/hooks/useValidParams'; -import { useSongDetailEntriesQuery } from '../queries'; +import { songDetailEntriesQueryOptions } from '../queries'; import type { Genre } from '../types/Song.type'; const useSongDetailEntries = () => { const { id: songIdParams, genre: genreParams } = useValidParams(); - const { - songDetailEntries, - queries: { isLoading: isLoadingSongDetailEntries }, - } = useSongDetailEntriesQuery(Number(songIdParams), genreParams as Genre); + const { data: songDetailEntries, isLoading: isLoadingSongDetailEntries } = useQuery( + songDetailEntriesQueryOptions(Number(songIdParams), genreParams as Genre) + ); const scrollIntoCurrentSong: React.RefCallback = useCallback((dom) => { if (dom !== null) dom.scrollIntoView({ behavior: 'instant', block: 'start' }); diff --git a/frontend/src/features/songs/queries/index.ts b/frontend/src/features/songs/queries/index.ts index 12454dc1..87ef32d8 100644 --- a/frontend/src/features/songs/queries/index.ts +++ b/frontend/src/features/songs/queries/index.ts @@ -1,4 +1,4 @@ -import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; +import { infiniteQueryOptions, queryOptions } from '@tanstack/react-query'; import { getExtraNextSongDetails, getExtraPrevSongDetails, @@ -6,22 +6,15 @@ import { } from '../remotes/songs'; import type { Genre } from '../types/Song.type'; -export const useSongDetailEntriesQuery = (songId: number, genre: Genre) => { - const { data: songDetailEntries, ...queries } = useQuery({ - queryKey: ['songDetailEntries'], +export const songDetailEntriesQueryOptions = (songId: number, genre: Genre) => + queryOptions({ + queryKey: ['songDetailEntries', songId, genre], queryFn: () => getSongDetailEntries(songId, genre), staleTime: Infinity, }); - return { songDetailEntries, queries }; -}; - -export const useExtraPrevSongDetailsInfiniteQuery = (songId: number, genre: Genre) => { - const { - data: extraPrevSongDetails, - fetchPreviousPage: fetchExtraPrevSongDetails, - ...infiniteQueries - } = useInfiniteQuery({ +export const extraPrevSongDetailsInfiniteQueryOptions = (songId: number, genre: Genre) => + infiniteQueryOptions({ queryKey: ['extraPrevSongDetails'], queryFn: ({ pageParam }) => getExtraPrevSongDetails(pageParam, genre), getPreviousPageParam: (firstPage) => firstPage[0]?.id ?? null, @@ -30,21 +23,11 @@ export const useExtraPrevSongDetailsInfiniteQuery = (songId: number, genre: Genr staleTime: Infinity, }); - return { extraPrevSongDetails, fetchExtraPrevSongDetails, infiniteQueries }; -}; - -export const useExtraNextSongDetailsInfiniteQuery = (songId: number, genre: Genre) => { - const { - data: extraNextSongDetails, - fetchNextPage: fetchExtraNextSongDetails, - ...infiniteQueries - } = useInfiniteQuery({ +export const extraNextSongDetailsInfiniteQueryOptions = (songId: number, genre: Genre) => + infiniteQueryOptions({ queryKey: ['extraNextSongDetails'], queryFn: ({ pageParam }) => getExtraNextSongDetails(pageParam, genre), getNextPageParam: (lastPage) => lastPage.at(-1)?.id ?? null, initialPageParam: songId, staleTime: Infinity, }); - - return { extraNextSongDetails, fetchExtraNextSongDetails, infiniteQueries }; -}; diff --git a/frontend/src/shared/hooks/useMutation.ts b/frontend/src/shared/hooks/useMutation.ts index f332c45f..ecdb990c 100644 --- a/frontend/src/shared/hooks/useMutation.ts +++ b/frontend/src/shared/hooks/useMutation.ts @@ -4,6 +4,9 @@ import { useLoginModalByError } from '@/features/auth/hooks/useLoginModalByError import AuthError from '@/shared/remotes/AuthError'; import type { ErrorResponse } from '../types/errorResponse'; +/** + * @deprecated react-query의 useMutation 훅을 사용해주세요. + */ // eslint-disable-next-line export const useMutation = (mutateFn: (...params: P) => Promise) => { const [data, setData] = useState(null);