리액트 쿼리의 invalidateQueries
를 사용하면 캐싱된 데이터의 재조회를 통해 클라이언트 측 상태를 업데이트 할 수 있다. 프로젝트 내 리뷰 등록의 예시를 들어 설명한다. api 요청은 post로 진행한다.
기존에는 댓글목록 정보를 상위 컴포넌트로부터 prop으로 받아와 업데이트 해준다. api 요청을 보내는 순간 isLoading 상태가 되어 로딩스피너를 띄운다. 정상적인 응답이 오면 댓글목록을 업데이트하여 보여준다.
import { useState, useRef } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
import CircularProgress from "@mui/material/CircularProgress";
import { useSelector } from "react-redux";
const ReviewRegistration = ({ reviews, setReviews }) => {
const { postId } = useParams();
const user = useSelector((state) => state.user);
const [isLoading, setIsLoading] = useState(false);
const [newReview, setNewReview] = useState("");
const [newReviewData, setNewReviewData] = useState({
content: newReview,
user_id: user.id,
recipe_id: postId,
last_name: user.last_name,
});
const reviewInputRef = useRef(null);
const handleReviewInputChange = () => {
setNewReview(reviewInputRef.current.value);
setNewReviewData({
content: reviewInputRef.current.value,
user_id: user.id,
recipe_id: postId,
last_name: user.last_name,
});
};
const handleReviewSubmit = (event) => {
event.preventDefault();
if (!user.id) {
alert("로그인 후 리뷰를 작성해주세요.");
return;
}
setIsLoading(true);
// REST: 리뷰 등록
const reviewPostURL = `${process.env.REACT_APP_SERVER}/api/review/`;
axios
?.post(reviewPostURL, newReviewData, {
headers: {
"Content-Type": "application/json",
},
})
?.then((res) => {
if (res.status === 200) {
setIsLoading(false);
setNewReview("");
setReviews([...reviews, newReviewData]);
}
});
};
return (
<>
<form className="flex-auto mr-2" onSubmit={handleReviewSubmit}>
{isLoading ? (
<div className="w-full h-full flex justify-center items-center">
<CircularProgress color="success" />
</div>
) : (
<input
onChange={handleReviewInputChange}
value={newReview}
className="w-full h-full py-0 pl-10 pr-24 bg-[#efefef] border-none rounded-3xl outline-2 outline-primary text-xl text-black/90"
type="text"
placeholder="리뷰 추가"
ref={reviewInputRef}
/>
)}
</form>
</>
);
};
export default ReviewRegistration;
우선 상위 컴포넌트에서 api 요청을 통해 리뷰 목록을 불러올 때, query key를 "recipe-reviews"
로 지정하여 캐싱한다. 아래 코드를 보기 전 리액트 쿼리 훅 중 useMutation
과 invalidateQueries
에 대해 이해한다.
React-Query Document > useMutation
useMutation
mutationFn
variables
를 받을 수 있는데 이는 useMutation
이 제공하는 함수인 mutate
가 이 함수에 전달하는 값이다. 폼 제출시 실행하는 핸들러인 handleReviewSubmit
내 createReview()
(mutate) 에 전달하는 값이 바로 그것이다.[onSuccess]
queryClient.invalidateQueries
onSuccess
와 결합하여 'mutation'이 성공적으로 일어난 경우에 수행한다.... 위와 동일한 import문 생략
import { useQueryClient, useMutation } from "@tanstack/react-query";
const ReviewRegistration = () => {
const { postId } = useParams();
const user = useSelector((state) => state.user);
const [newReview, setNewReview] = useState("");
const [newReviewData, setNewReviewData] = useState({
content: newReview,
user_id: user.id,
recipe_id: postId,
last_name: user.last_name,
});
const reviewInputRef = useRef(null);
const handleReviewInputChange = () => {
setNewReview(reviewInputRef.current.value);
setNewReviewData({
content: reviewInputRef.current.value,
user_id: user.id,
recipe_id: postId,
last_name: user.last_name,
});
};
const { mutate: createReview, isPending } = usePostReview();
const handleReviewSubmit = (event) => {
event.preventDefault();
if (!user.id) {
alert("로그인 후 리뷰를 작성해주세요.");
event.preventDefault();
return;
}
createReview(newReviewData);
setNewReview("");
};
return (
<>
<form className="flex-auto mr-2" onSubmit={handleReviewSubmit}>
{isPending ? (
<div className="w-full h-full flex justify-center items-center">
<CircularProgress color="success" />
</div>
) : (
<input
onChange={handleReviewInputChange}
value={newReview}
className="w-full h-full py-0 pl-10 pr-24 bg-[#efefef] border-none rounded-3xl outline-2 outline-primary text-xl text-black/90"
type="text"
placeholder="리뷰 추가"
ref={reviewInputRef}
/>
)}
</form>
</>
);
};
// REST: 리뷰 등록
const usePostReview = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (newReviewData) => {
const reviewPostURL = `${process.env.REACT_APP_SERVER}/api/review/`;
return await axios.post(reviewPostURL, newReviewData);
},
onSuccess: () => {
queryClient.invalidateQueries(["recipe-reviews"]);
},
});
};
export default ReviewRegistration;