리액트에서 부모 컴포넌트를 저장할때 자식 컴포넌트도 별도로 저장해야하는 상황에 부딪혔다. api를 별도로 보내야하고 컴포넌트 파일도 분리되어 있지만 저장 버튼을 누르면 한번에 저장 및 업데이트를 시키고 싶었다.
문제 코드 )
//parent.tsx
export default function Parent() {
const router = useRouter();
const id = String(router.query.id || "");
const methods = useForm({
mode: "onChange",
});
const {
register,
handleSubmit,
getValues,
reset,
formState: { errors },
} = methods;
const { data } = useParentQuery(id);
useEffect(() => {
if (id) reset(data);
}, [data]);
const onSubmit = async (submitData: any, e: any) => {
e.preventDefault();
const formData: FormData = new FormData(e.target);
mutateAsync(formData)
.then((res) => {
alert("저장 완료");
window.location.reload();
})
.catch((e) => {
alert("에러가 발생했습니다 : " + e.response.data.message);
});
};
return (
...
);
}
//children.tsx
export default function Children() {
const { data } = useChildrenQuery(parentId);
const [arrayData, setArrayData] = useState(data || []);
useEffect(() => {
if (!parentId) {
return;
}
if (data) {
setArrayData(data);
}
}, [data]);
const { mutate: updateObject } = useObjectMutation();
const methods = useForm({
mode: "onChange",
});
const {
register,
control,
handleSubmit,
getValues,
setValue,
watch,
reset,
formState: { errors },
} = methods;
const changeHandler = (e: any, index: number) => {
const id = e.target.id;
const value = e.target.value;
setArrayData((prev: any) =>
prev.map((item: any, i: number) =>
i === index ? { ...item, [id]: value } : item
)
);
};
const onSubmit = () => {
arrayData.map((item: any, index: number) => {
const formData: FormData = new FormData();
updateObject({ id: String(promotionId), formData: formData });
});
};
return (
<>
</>
)
}
mutateAsync(formData)
.then((res) => {
const pid = res.data ? res.data : id;
alert("저장 완료");
window.location.reload();
})
.catch((e) => {
alert("에러가 발생했습니다 : " + e.response.data.message);
});
//useRef사용해서 자식 컴포넌트 값 접근하기
const childrenRef = useRef<ISubmitHandle>(null);
//자식 컴포넌트 유효성 검사
const childrenValid = childrenRef.current?.valid();
if (childrenValid != "") {
alert(childrenValid);
return;
}
//부모컴포넌트 저장 후 자식컴포넌트에 전달
mutateAsync(formData)
.then((res) => {
const pid = res.data ? res.data : id;
childrenRef.current?.submit(pid);
alert("저장 완료");
window.location.reload();
})
.catch((e) => {
alert("에러가 발생했습니다 : " + e.response.data.message);
});
const Children = React.forwardRef<ISubmitHandle, Props>(
({ parentId }, ref) => {
const customValidation = () => {
}
useImperativeHandle(ref, () => ({
submit(pid: string) {
parentId = pid;
handleSubmit(onSubmit)();
},
valid() {
return customValidation();
},
}));
}
);
해결된 코드)
//parent.tsx
export default function Parent() {
const router = useRouter();
const id = String(router.query.id || "");
const methods = useForm({
mode: "onChange",
});
const {
register,
handleSubmit,
getValues,
reset,
formState: { errors },
} = methods;
const childrenValid = childrenRef.current?.valid();
if (childrenValid != "") {
alert(childrenValid);
return;
}
const { data } = useParentQuery(id);
useEffect(() => {
if (id) reset(data);
}, [data]);
const onSubmit = async (submitData: any, e: any) => {
e.preventDefault();
const formData: FormData = new FormData(e.target);
const childrenRef = useRef<ISubmitHandle>(null);
mutateAsync(formData)
.then((res) => {
const pid = res.data ? res.data : id;
childrenRef.current?.submit(pid);
alert("저장 완료");
window.location.reload();
})
.catch((e) => {
alert("에러가 발생했습니다 : " + e.response.data.message);
});
return (
...
);
}
//children.tsx
const Children = React.forwardRef<ISubmitHandle, Props>(
({ parentId }, ref) => {
const { data } = useChildrenQuery(parentId);
const [arrayData, setArrayData] = useState(data || []);
useEffect(() => {
if (!parentId) {
return;
}
if (data) {
setArrayData(data);
}
}, [data]);
const { mutate: updateObject } = useObjectMutation();
const methods = useForm({
mode: "onChange",
});
const {
register,
control,
handleSubmit,
getValues,
setValue,
watch,
reset,
formState: { errors },
} = methods;
const changeHandler = (e: any, index: number) => {
const id = e.target.id;
const value = e.target.value;
setArrayData((prev: any) =>
prev.map((item: any, i: number) =>
i === index ? { ...item, [id]: value } : item
)
);
};
const onSubmit = () => {
arrayData.map((item: any, index: number) => {
const formData: FormData = new FormData();
updateObject({ id: String(promotionId), formData: formData });
});
};
const customValidation = () => {
}
useImperativeHandle(ref, () => ({
submit(pid: string) {
parentId = pid;
handleSubmit(onSubmit)();
},
valid() {
return customValidation();
},
}));
return (
<>
</>
)
}