Skip to content

Instantly share code, notes, and snippets.

@hongggyelim
Last active April 25, 2025 05:14
Show Gist options
  • Save hongggyelim/c75918e6ff599a4db17dbe45b58bcc0d to your computer and use it in GitHub Desktop.
Save hongggyelim/c75918e6ff599a4db17dbe45b58bcc0d to your computer and use it in GitHub Desktop.
단계별 폼 데이터 처리
export default function AddFormPage() {
const router = useRouter();
// 리액트 훅폼에서 관리할 데이터 타입 지정 및 메서드 호출 (상위 컴포넌트 = useForm 사용)
const methods = useForm<SubmitFormDataType>({
mode: "onChange",
shouldUnregister: false,
defaultValues: {
isPublic: true,
hourlyWage: 0,
isNegotiableWorkDays: false,
workDays: [],
workEndTime: "",
workStartTime: "",
workEndDate: "",
workStartDate: "",
location: "",
preferred: "",
age: "",
education: "",
gender: "",
numberOfPositions: 0,
recruitmentEndDate: "",
recruitmentStartDate: "",
description: "",
title: "",
imageUrls: [],
},
});
const {
setValue,
handleSubmit,
watch,
formState: { isDirty, isValid },
} = methods;
const queryClient = useQueryClient();
// 훅폼에서 관리하는 전체 데이터를 가져오는 함수
const currentValues: SubmitFormDataType = watch();
// 각각의 탭 작성중 여부
const { isEditingRecruitContent, isEditingRecruitCondition, isEditingWorkCondition } = useEditing(currentValues);
// 유저 권한 확인
const { user, isLoading } = useUser();
// tab 선택 시 Url params 수정 & 하위 폼 데이터 임시저장
const searchParams = useSearchParams();
const currentParam = searchParams.get("tab");
// 폼데이터 임시 저장 함수
const onTempSave = () => {
tempSave("tempAddFormData", currentValues);
};
const handleOptionChange = (option: string) => {
if (option !== currentParam && isDirty) {
onTempSave();
}
const params = {
"모집 내용": "recruit-content",
"모집 조건": "recruit-condition",
"근무 조건": "work-condition",
}[option];
router.replace(`/addform?tab=${params}`);
};
// 임시저장 데이터 로드 함수
const loadTempData = () => {
const tempData = localStorage.getItem("tempAddFormData");
if (tempData) {
const parsedData: SubmitFormDataType = JSON.parse(tempData);
// 기본 필드들 설정
Object.entries(parsedData).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
setValue(key as keyof SubmitFormDataType, value);
}
});
}
};
if (isLoading) {
return <LoadingSpinner />;
}
if (user?.role !== "OWNER") {
toast.error("사장님만 워크폼을 작성할 수 있습니다.");
router.replace("/work-list");
}
const renderChildren = () => {
switch (currentParam) {
case "recruit-content":
return <RecruitContentSection key="recruitContent" />;
case "recruit-condition":
return <RecruitConditionSection key="recruitCondition" />;
case "work-condition":
return <WorkConditionSection key="workCondition" />;
default:
return <RecruitContentSection key="recruitContent" />;
}
};
return (
<FormProvider {...methods}>
<div className="relative pb-10 lg:pb-0">
<aside className="flex flex-col items-center justify-between rounded-[24px] bg-background-200 lg:fixed lg:left-[108px] lg:top-[64px] lg:m-10 lg:h-[80vh] lg:p-10">
<TabMenuDropdown
options={[
{
label: "모집 내용",
isEditing: isEditingRecruitContent || currentParam === "recruit-content",
},
{ label: "모집 조건", isEditing: isEditingRecruitCondition || currentParam === "recruit-condition" },
{ label: "근무 조건", isEditing: isEditingWorkCondition || currentParam === "work-condition" },
]}
onChange={handleOptionChange}
currentParam={currentParam || ""}
/>
<div className="absolute -bottom-[160px] mb-20 flex w-full flex-col gap-2 lg:relative lg:bottom-0 lg:mb-0">
<Button
type="button"
variant="outlined"
width="md"
color="orange"
className="lg: h-[58px] w-[320px] border bg-background-100 lg:h-[72px] lg:w-full lg:text-xl lg:leading-8"
onClick={() => tempSave("tempAddFormData", currentValues)}
>
임시 저장
</Button>
<Button
type="submit"
variant="solid"
width="md"
color="orange"
className="lg: h-[58px] w-[320px] lg:h-[72px] lg:w-full lg:text-xl lg:leading-8"
disabled={!isValid}
onClick={handleSubmit(() => mutation.mutate())}
>
{mutation.isPending ? <DotLoadingSpinner /> : "작성 완료"}
</Button>
</div>
</aside>
{renderChildren()}
</div>
</FormProvider>
);
}
/* 하위 폼 예시*/
export default function RecruitConditionSection() {
const {
register,
formState: { errors },
} = useFormContext();
return (
<div className="relative">
<form className="my-8 flex flex-col gap-4">
<Label>모집인원</Label>
<InputDropdown
{...register("numberOfPositions", { required: "모집 인원을 선택해주세요", valueAsNumber: true, min: 1 })}
options={["1", "2", "3", "4", "5", "직접 입력"]}
errormessage={errors.numberOfPositions?.message as string}
/>
<Label>우대사항</Label>
<InputDropdown
{...register("preferred", { required: "우대사항을 선택해주세요" })}
options={["유사 업무 경험 우대", "운전 가능", "직접 입력"]}
errormessage={errors.preferred?.message as string}
/>
</form>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment