[React] Form 외부 버튼으로 입력값 전달하기

zzincode·2025년 3월 5일
0

ReviewZIP

목록 보기
5/7

해당 프로젝트의 Order페이지에 Input을 입력하는 요소가 존재합니다.
결제하기(Submit) 버튼을 누르면 input에 입력한 값들이 다음페이지로 넘어가면서 값들도 같이 넘어갈 수 있도록 구현하려고 합니다.

버튼이 form 외부에 있을 때 제출 이벤트 발생

일반적으로 <form> 안에 있는 버튼을 클릭하거나 입력 필드에서 Enter 키를 입력하면 제출 이벤트가 발생합니다.

현재 구현된 Order 페이지에서는 Buttonform 외부에 위치해 있습니다.

이를 해결하려면 <form>id 값과 버튼의 form 값을 동일하게 설정하면 제출 이벤트를 발생 시킬 수 있습니다.

<form className="OrderForm" id="order-form" onSubmit={handleSubmit}>

<Button styleType="brand-solid" block form="order-form">

⚠️ <Button> 컴포넌트를 사용할 시에는 form 관련 props를 전달받도록 설정되어 있어야 합니다.

 //Button.js
    <button
      className={[`btn ${btnType} ${btnSize}`]}
      onClick={onClick}
      form={form}
    >
      {text}
    </button>
  );

export default Button;
//order.js
<Button styleType="brand-solid" form="order-form">...

저는 처음에는 Button 컴포넌트에서 form 관련 props를 설정해주지 않은 상태로 <Button>form 속성을 추가해 작동하지 않아 당황하였습니다ㅎㅎ

ref를 활용한 form 입력값 조회

useRef를 사용하면 직접 DOM 요소에 접근하여 값을 가져올 수 있습니다.

form 요소에서 ref를 사용하면 onChange 없이도 입력값을 쉽게 조회 가능합니다. (불필요한 렌더링 방지)

사용자 입력 값을 저장할 ref 객체 생성

 const userNameRef = useRef(null);
 const userTelRef = useRef(null);

submit이 작동하게 되면

//OrderForm.js

const handleSubmit = e => {
    e.preventDefault();
    const formData = {
      name: userNameRef.current.value, //①ref를 통해 이름 입력값 가져오기
      tel: userTelRef.current.value, // ②ref를 통해 전화번호 입력값 가져오기
      payment: checked, // ⓷선택된 결제 수단 가져오기
    };
    onSubmit(formData); //④ 상위 컴포넌트로 데이터 전달
  };
  1. ref를 통해 이름 입력값 가져오기 - userNameRef form에 연결
 <FormControl label={'이름'} htmlFor={'userName'} required>
    <input
      type="text"
      id="userName"
      name="userName"
      placeholder="이름"
      ref={userNameRef}
      autoFocus
      required
    />
  </FormControl>
  1. ref를 통해 전화번호 입력값 가져오기 -useTelref form에 연결
<FormControl label={'전화번호'} htmlFor={'userTel'} required>
	  <input
	    type="text"
	    id="userTel"
	    name="userTel"
	    placeholder="01000000000"
	    pattern="^\d{2,3}\d{3,4}\d{4}$"
	    ref={userTelRef}
	    required
	  />
</FormControl>
  1. 선택된 결제 수단 가져오기
    → Radio 버튼은 하나의 그룹 내에서 한번에 하나만 선택될 수 있기 때문에 current.value를 사용하지 않아도 선택된 값을 쉽게 확인 가능

  2. OrderForm의 onSubmit을 Order 컴포넌트에서 받아서 처리

    <OrderForm onSubmit={handleSubmit} />
    //Order.js
    
    const handleSubmit = formData => {
        setCredit(true);
        setTimeout(() => {
          setCredit(false);
          navigate('/completedOrder', { state: formData });
        }, '3000');
      };

    → completedOrder 페이지로 넘어가면서 입력값들을 해당 페이지에 전달해줌


전체 코드


const OrderForm = ({ onSubmit }) => {
  const [checked, setChecked] = useState(paymentMethod[0]);
  const userNameRef = useRef(null);
  const userTelRef = useRef(null);

  const handleSubmit = e => {
    e.preventDefault();
    const formData = {
      name: userNameRef.current.value,
      tel: userTelRef.current.value,
      payment: checked,
    };
    onSubmit(formData);
  };
  return (
    <form className="forms" id="order-form" onSubmit={handleSubmit}>
      <AccordionList title={'개인정보'} boolean={true}>
        <FormControl label={'이름'} htmlFor={'userName'} required>
          <input
            type="text"
            id="userName"
            name="userName"
            placeholder="이름"
            ref={userNameRef}
            autoFocus
            required
          />
        </FormControl>
        <FormControl label={'전화번호'} htmlFor={'userTel'} required>
          <input
            type="text"
            id="userTel"
            name="userTel"
            placeholder="01000000000"
            pattern="^\d{2,3}\d{3,4}\d{4}$"
            ref={userTelRef}
            required
          />
        </FormControl>
      </AccordionList>
      <AccordionList title={'결제수단'} boolean={true}>
        <section className="paymentMethod">
          {paymentMethod.map(method => (
            <FormControl
              key={method}
              label={method}
              htmlFor={method}
              sr_only={'sr-only'}
            >
              <Button
                styleType={checked === method ? 'brand' : 'brandSolid'}
                onClick={e => {
                  e.preventDefault();
                  setChecked(method);
                }}
                text={method}
              />
              <input
                type="radio"
                name="paymentMethod"
                id={method}
                value={method}
                checked={checked === method}
                onChange={() => setChecked(method)}
              />
            </FormControl>
          ))}
        </section>
      </AccordionList>
    </form>
  );
};

export default OrderForm;
const Order = () => {
  const [credit, setCredit] = useState(false);
  const navigate = useNavigate();

  const handleSubmit = formData => {
    setCredit(true);
    setTimeout(() => {
      setCredit(false);
      navigate('/completedOrder', { state: formData });
    }, '3000');
  };

  return (
    <div className="Order">
      <Page
        header={<Header header={'결제'} back />}
        footer={
          <Button
            styleType={'full'}
            styleSize={'large'}
            text={'결제하기'}
            form="order-form"
          />
        }
      >
        /* ... */
        <OrderForm onSubmit={handleSubmit} />
        /* ... */
      </Page>
      {credit && (
        <PopUp>
          <BeatLoader />
          <div>
            <p>결제 진행 중입니다.</p>
            <p>잠시만 기다려 주세요.</p>
          </div>
        </PopUp>
      )}
    </div>
  );
};

export default Order;

입력창
결과창

0개의 댓글