// useUpdateMutation
export const useUpdateMutation = () => {
// 1. useRouter : Next.js의 라우터 객체 가져오기
const router = useRouter();
// 2. useQueryClient hook : query client 객체 가져오기
const queryClient = useQueryClient();
// 3. useMutation : mutationFn, onSuccess, onError, onSettled 객체 반환
return useMutation({
// 4. updatePostDetail : 백 서버에 포스트 데이터 업데이트 요청
mutationFn: updatePostDetail,
// 5. queryClient : 캐시된 데이터 무효화 후 업데이트된 포스트의 상세 페이지로 redirect
onSuccess: async (_, variables) => {
await queryClient.invalidateQueries({ queryKey: ["updatedpost"] });
router.push(`/posts/${variables.postId}`);
},
// mutation 작업이 실패했을 때
onError: (err) => {
console.error(err);
},
// mutation 작업이 완료되었을 때
onSettled: () => {
console.log("완료");
},
});
> };
// 게시글 수정 페이지 (EditPost)
export default function EditPost({ id }: { id: string }) {
// pwdChecked state 생성
const [pwdChecked, setPwdChecked] = useState(true);
// 기존 게시글 데이터 불러오기
const { data: oldPost, isLoading: isReadLoading } = useQuery(
["postDetail", id],
async () => {
const response = await axios.get(`/posts/${id}`);
return response.data;
}
);
// useUpdateMutation 커스텀 훅 가져오기 (구조분해 할당)
const { data, isLoading, mutate, mutateAsync } = useUpdateMutation();
const {
register,
handleSubmit,
formState: { errors },
setError,
setValue,
control,
} = useForm<IPostForm>({
defaultValues: {
nickname: oldPost?.nickname || "",
title: oldPost?.title || "",
content: oldPost?.content || "",
},
});
const onValid: SubmitHandler<IPostForm> = async (data) => {
if (!data.nickname || !data.title || !data.content) {
const errMsg: { [key: string]: string } = {};
if (!data.nickname) errMsg.nickname = "이름 또는 닉네임을 입력해 주세요.";
if (!data.title) errMsg.title = "제목을 입력해 주세요.";
if (!data.content) errMsg.content = "내용을 입력해 주세요.";
const setErrors = (errors: Record<string, string>) => {
Object.entries(errors).forEach(([key, value]) => {
setError(key as "nickname" | "title" | "content", {
message: value,
type: "required",
});
});
};
setErrors(errMsg);
return;
}
const updateData: IUpdatePostForm = {
postId: id as string,
...data,
};
await mutateAsync(updateData);
};
return (
<>
{isReadLoading && <Loading />}
{pwdChecked ? (
<PostUpdatePwd id={id} setPwdChecked={setPwdChecked} />
) : (
oldPost && (
<div className="w-full px-4">
(이하 생략)
비밀번호 체크 시 입력 받은 비밀번호가 부모 컴포넌트인 EditPost()에 전달되지 않아, data.password 가 undefined 상태로 put 되어 axios 400 에러가 발생했습니다.
이를 해결하기 위해 Props를 이용하여 함수를 전달하고 호출하여, PostUpdatePwd 컴포넌트에서 입력받은 password를 부모 컴포넌트에서 사용할 수 있도록 코드를 수정했습니다.
import { useState } from "react";
import PostUpdatePwd from "./PostUpdatePwd";
function ParentComponent() {
const [password, setPassword] = useState("");
const handlePassword = (password: string) => {
setPassword(password);
};
return (
<div>
<PostUpdatePwd id="1" setPwdChecked={() => {}} handlePassword={handlePassword} />
<div>{password}</div>
</div>
);
}
interface PostUpdatePwdProps {
id: string;
setPwdChecked: Dispatch<SetStateAction<boolean>>;
handlePassword: (password: string) => void;
}
export default function PostUpdatePwd({
id,
setPwdChecked,
handlePassword,
}: PostUpdatePwdProps) {
// ...
const onValid: SubmitHandler<IUpdatePostCheckForm> = async (data) => {
// ...
if (isSuccess) {
handlePassword(data.password); // 부모 컴포넌트에서 전달한 함수를 호출해 'password'의 상태를 입력 받은 password로 전환
setPwdChecked(false);
} else {
// ...
}
};
// ...
}
위와 같이 Props와 함수를 이용하여 자식 컴포넌트에서 입력 받은 비밀번호를 부모 컴포넌트의 함수를 호출해 활용할 수 있습니다.