저번에 서버에서 데이터를 가져오는 용도로만 React-query를 사용했다면 이번에는 변경 요청을 보내는 작업을 처리해보록 할려고 한다.
useMutation를 사용하면, 서버에 데이터 변경 요청을 보내는 작업을 처리할 수 있다. 이때 해당 데이터 변경이 성공하면 새로운 데이터를 가져오거나 캐시를 업데이트하여 컴포넌트가 실시간으로 업데이트되도록 도와준다.
여기서는 댓글을 추가하고 삭제하는 hook을 useMutation을 활용해 만들었다.
function useCommentQuery(token, postId) {
const getComment = async () => {
return await GET_API(token, `/post/${postId}/comments?limit=100`);
};
const commentQuery = useQuery({
queryKey: ['comment'],
queryFn: () => getComment(),
});
return [commentQuery.data, commentQuery.isLoading, commentQuery.isError];
}
function useAddCommentMutation(token, postId) {
const queryClient = useQueryClient();
const addComment = async (content) => {
const bodyData = {
'comment': {
'content': content,
},
};
return await POST_API(token, `/post/${postId}/comments`, bodyData);
};
return useMutation((content) => addComment(content), {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['comment'],
refetchType: 'active',
});
},
});
}
function useDeleteCommentMutation(token, postId, commentId) {
const queryClient = useQueryClient();
const deleteComment = async () => {
return await DELETE_API(token, `/post/${postId}/comments/${commentId}`);
};
return useMutation(() => deleteComment(), {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['comment'],
refetchType: 'active',
});
},
});
}
export default function InputComment({ image }) {
const { id } = useParams();
const [inputVal, setInputVal] = useState('');
const [token, setToken] = useRecoilState(userToken);
const [userImage, setUserImage] = useRecoilState(userimage);
const handleSubmit = async (e) => {
e.preventDefault();
const postCmt = await writeCommentAPI(token, id, inputVal);
setInputVal('');
location.reload();
};
const handleInputChange = (e) => {
setInputVal(e.target.value);
};
return (...);
}
export default function ViewComment({ comment, post_id }) {
const profileLink = `/profile/${comment.author.accountname}`;
const date = comment.createdAt;
const displayDate = `${date.slice(0, 4)}.${parseInt(
date.slice(5, 7),
)}.${parseInt(date.slice(8))} ${date.slice(11, 13)}:${date.slice(14, 16)}`;
const [token, setToken] = useRecoilState(userToken);
const [accountName, setAccountName] = useRecoilState(accountname);
const [isBsOpen, setIsBsOpen] = useRecoilState(isBottomSheetOpen);
const [bsItems, setBsItems] = useRecoilState(bottomSheetItems);
const [isModal, setIsModal] = useRecoilState(isModalOpen);
const [modalItem, setModalItem] = useRecoilState(modalItems);
const onMoreClick = () => {
setIsBsOpen((prev) => !prev);
if (accountName === comment.author.accountname) {
const onCommentDelete = () => {
setIsModal(true);
const deleteComment = async () => {
const data = await deleteCommentAPI(token, post_id, comment.id);
setIsModal(true);
setModalItem([
'해당 댓글이 삭제되었습니다.',
'확인',
function () {
location.reload();
},
]);
};
setModalItem(['해당 댓글을 삭제할까요?', '삭제', deleteComment]);
};
const bsItem = [['삭제', onCommentDelete]];
setBsItems(bsItem);
}
};
return (...);
}
deleteComment
함수 안에서 deleteCommentAPI
를 사용해 댓글을 삭제하고 있다. export default function InputComment({ image }) {
const { id } = useParams();
const [inputVal, setInputVal] = useState('');
const [token] = useRecoilState(userToken);
const [userImage, setUserImage] = useRecoilState(userimage);
const addCommentMutate = useAddCommentMutation(token, id);
const handleSubmit = async (e) => {
e.preventDefault();
addCommentMutate.mutateAsync(inputVal);
setInputVal('');
};
const handleInputChange = (e) => {
setInputVal(e.target.value);
};
return (...);
}
writeCommentAPI
함수대신 addCommentMutation
훅을 사용해 댓글을 추가한다. export default function ViewComment({ comment, post_id }) {
// 일단 버튼을 누르면 modal이 나오도록 해둠
// modal이 false면 안나오고 버튼을 누르면 setModal로 true로 바뀌며 모달 생성
// 기본값은 false
const profileLink = `/profile/${comment.author.accountname}`;
const date = comment.createdAt;
const displayDate = `${date.slice(0, 4)}.${parseInt(
date.slice(5, 7),
)}.${parseInt(date.slice(8))} ${date.slice(11, 13)}:${date.slice(14, 16)}`;
const [token] = useRecoilState(userToken);
const [accountName] = useRecoilState(accountname);
const [isBsOpen, setIsBsOpen] = useRecoilState(isBottomSheetOpen);
const [bsItems, setBsItems] = useRecoilState(bottomSheetItems);
const [isModal, setIsModal] = useRecoilState(isModalOpen);
const [modalItem, setModalItem] = useRecoilState(modalItems);
const deleteCommentMutate = useDeleteCommentMutation(
token,
post_id,
comment.id,
);
const onMoreClick = () => {
setIsBsOpen((prev) => !prev);
if (accountName === comment.author.accountname) {
const onCommentDelete = () => {
setIsModal(true);
const deleteComment = async () => {
await deleteCommentMutate.mutateAsync();
setIsModal(true);
setModalItem(['해당 댓글이 삭제되었습니다.', '확인', function () {}]);
};
setModalItem(['해당 댓글을 삭제할까요?', '삭제', deleteComment]);
};
const bsItem = [['삭제', onCommentDelete]];
setBsItems(bsItem);
}
};
return (...);
}
deleteCommentAPI
함수 대신 useDeleteCommentMutation
훅을 사용해 댓글을 삭제한다. 댓글을 추가하거나 삭제하면 서버에는 반영되지만 화면상에는 바로 반영되지 않아 일단은 페이지 리로드를 강제로 하는 location.reload()
함수를 추가했다. 하지만 댓글을 추가하거나 삭제할때마다 페이지 리로드하는게 비효율적이라고 느껴졌고 매번 스켈레톤 UI를 다시 보는 것이 사용자 경험에 좋지 않았다.
React-query 도입 후 페이지 리로드를 하지 않아도 서버에 반영된 데이터가 화면에도 바로 반영이 되어서 사용자 경험을 향상시켰다.
댓글 추가 이벤트 (React-query 적용 전) | 댓글 추가 이벤트 (React-query 적용 후) |
---|---|
![]() | ![]() |
댓글 추가 후 페이지를 reload하지 않으면 화면 상에 바로 뜨지 않기에 댓글 추가 후 강제로 reload하도록 했다. | 댓글 추가 후 페이지를 reload하지 않아도 바로 추가 되는 것을 확인할 수 있다. |