useImperative

YJS·2023년 9월 21일
0

😖문제 상황

리액트에서 부모 컴포넌트를 저장할때 자식 컴포넌트도 별도로 저장해야하는 상황에 부딪혔다. 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 (
      <>
    </>
    )
}
        

🤓문제 해결 과정

step1. 부모 컴포넌트를 저장하는 api를 먼저 요청하고 res로 부모 컴포넌트의 id를 받아온다

	 mutateAsync(formData)
      .then((res) => {
        const pid = res.data ? res.data : id;
        alert("저장 완료");
        window.location.reload();
      })
      .catch((e) => {
        alert("에러가 발생했습니다 : " + e.response.data.message);
      });

step2. 받아온 부모 컴포넌트의 id를 자식 컴포넌트에 전달한다

//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);
      });

step3. 자식 컴포넌트의 저장 메서드를 부모 컴포넌트 저장 이후에 호출한다

	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 (
      <>
    </>
    )
}
profile
우당탕탕 개발 일기

0개의 댓글