Last active
April 25, 2025 05:14
-
-
Save hongggyelim/c75918e6ff599a4db17dbe45b58bcc0d to your computer and use it in GitHub Desktop.
단계별 폼 데이터 처리
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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