본문 바로가기

공부기록/Typescript

# 58 Form 래퍼 컴포넌트 타입지정

 

 

 

 

import { type ComponentPropsWithoutRef } from "react";

type FormProps = ComponentPropsWithoutRef<"form">;

const Form = (props: FormProps) => {
  return <form {...props}>{props.children}</form>;
};

export default Form;

 

 

 

이것은 정상작동된다 . 

 

그러나 button을 children으로 넣어주었을 때 

button을 클릭시 submit하는 액션이 동작하도록 하고 싶다. 

 

 

const Form = (props: FormProps) => {
  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    const data = new FormData(e.currentTarget);
  };
  return (
    <form onSubmit={handleSubmit} {...props}>
      {props.children}
    </form>
  );
};

 

e는 FormEvent이며 HTMLFormElement에서 동작하는 이벤트 

 

FromData는 input에 있는 모든 value를 formData로 가져옴 

반드시 name값을 지정해 주어야 함 

 

formData -> Object 변환


 

formData에 접근하려면 form.get메서드를 써야하므로 

불편할 수있음 

 

그래서 변환해 줌

 

    const data = Object.fromEntries(formData);

 

 

 

전달받은 onSave메서드에 저장


 

 

import { FormEvent, type ComponentPropsWithoutRef } from "react";

type FormProps = ComponentPropsWithoutRef<"form"> & {
  onSave: (value: unknown) => void;
};

const Form = (props: FormProps) => {
  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    const formData = new FormData(e.currentTarget);
    const data = Object.fromEntries(formData);
    props.onSave(data);
  };
  return (
    <form onSubmit={handleSubmit} {...props}>
      {props.children}
    </form>
  );
};

export default Form;

 

 

실 사용 예시 

 

function App() {
  const testRef = useRef(null);
  const handleSave = (data: unknown) => {};
  return (
    <main>
      <Form onSave={handleSave}>

 

 

 

 

접근되나, 에러 발생


 

 

 

위의 에러문구는 

내가 알 수없는 곳에서 onSave가 사용되고 있다는것. 

예상 못한것.

 

 

 

그러면 객체 구조분해할당을 통해 onSave만 제외한 나머지 otherProps로 넘겨줌 

 

 

import { FormEvent, type ComponentPropsWithoutRef } from "react";

type FormProps = ComponentPropsWithoutRef<"form"> & {
  onSave: (value: unknown) => void;
};

const Form = ({ onSave, children, ...otherProps }: FormProps) => {
  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    const formData = new FormData(e.currentTarget);
    const data = Object.fromEntries(formData);
    onSave(data);
  };
  return (
    <form onSubmit={handleSubmit} {...otherProps}>
      {children}
    </form>
  );
};

export default Form;

 

 

 

리셋 기능 추가 


 

import { FormEvent, type ComponentPropsWithoutRef, useRef } from "react";

type FormProps = ComponentPropsWithoutRef<"form"> & {
  onSave: (value: unknown) => void;
};

const Form = ({ onSave, children, ...otherProps }: FormProps) => {
  const form = useRef<HTMLFormElement>(null);

  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    const formData = new FormData(e.currentTarget);
    const data = Object.fromEntries(formData);
    onSave(data);
    form.current?.reset();
  };
  return (
    <form onSubmit={handleSubmit} {...otherProps} ref={form}>
      {children}
    </form>
  );
};

export default Form;

 

 

 

 

useImperativeHandle api


form을 호출한 컴포넌트에서 

form.current.clear()과 같은 메서드를 호출하고 싶음...?

호출 가능한 함수 노출 하기

 

 

forwardedRef를 수신

 

 

useImperative는

첫번째 인수 : ref,

두번째 인수 : object를 반환하는 함수

 

외부 컴포넌트에서 

해당 컴포넌트를 호출 할 수 있게 해줌 

 

전체 컴포넌트에 접근하지 않고 

useImperativeHandle에서 반환한 object를 참조 할 수 있음 

 

 

import {
  FormEvent,
  type ComponentPropsWithoutRef,
  useRef,
  useImperativeHandle,
  forwardRef,
} from "react";

export type FormHandle = {
  clear: () => void;
};

type FormProps = ComponentPropsWithoutRef<"form"> & {
  onSave: (value: unknown) => void;
};

const Form = forwardRef<FormHandle, FormProps>(
  ({ onSave, children, ...otherProps }: FormProps, ref) => {
    const form = useRef<HTMLFormElement>(null);

    useImperativeHandle(ref, () => {
      return {
        clear() {
          console.log("CLEARING");
          form.current?.reset();
        },
      };
    });

    const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
      e.preventDefault();

      const formData = new FormData(e.currentTarget);
      const data = Object.fromEntries(formData);
      onSave(data);
      form.current?.reset();
    };
    return (
      <form onSubmit={handleSubmit} {...otherProps} ref={form}>
        {children}
      </form>
    );
  }
);

export default Form;