export default function Page() {
const saveName = async (formDat:FormData)=>{
"use server";
const name = formData.get('name');
await saveDB({ name: name });
}
return (
)
}
"use server"
try {
const res = await fetch(`${API_URL}/review`, {
method: 'POST',
body: JSON.stringify({
}),
});
console.log(res.status);
revalidatePath(`/book/${bookId}`);
} catch (err) {
console.error(err);
return;
}
revalidatePath
revalidatePath(`/book/${bookId}`);
// 1. 특정 주소의 해당하는 페이지만 재검증.
revalidatePath(`/book/[id]`, 'page');
// 2. 특정 경로의 모든 동적 페이지를 재검증.
revalidatePath(`/(with-searchbar)`, 'layout');
// 3. 특정 레이아웃을 갖는 모든 페이지를 재검증.
revalidatePath(`/`, 'layout');
// 4. 모든 데이터 재검증
revalidateTag
: 태그를 기준으로 데이터 캐시 재검증. revalidateTag(`review-${bookId}`);
// 5. 태그를 기준으로 데이터 캐시 재검증.
const response = await fetch(`${API_URL}`, {
next: { tags: [`review-${bookId}`] },
});
useActionState
: 폼 태그의 상태를 핸들링하는 여러 기능 보유.const [state, formAction, isPending] = useActionState(
createReviewAction, // 핸들링하려는 폼의 action 함수
null // 폼 상태의 초기값
);
...
<form className={style.form_container} action={formAction}>
<input name="bookId" value={bookId} hidden />
<textarea
disabled={isPending}
..
/>
<div className={style.submit_container}>
<button disabled={isPending} type="submit">
{isPending ? "..." : "작성하기"}
</button>
</div>
</form>
// createReviewAction.ts
export const createReviewAction = async (_: any, formData: FormData) => {
// 첫번째 인자로 status를 받아야 함.(사용하지 않을 땐 _ 로 대체)
if (!content || !author) {
return {
status: false,
error: '리뷰 내용과 작성자를 입력해주세요.',
};
}
try {
const res = await fetch(`${API_URL}/review`, {
});
...
return {
status: true,
error: '',
};
} catch (err) {
return {
status: false,
error: `리뷰 저장에 실패했습니다. : ${err}`,
};
}
};