해당 프로젝트의 Order페이지에 Input을 입력하는 요소가 존재합니다.
결제하기(Submit) 버튼을 누르면 input에 입력한 값들이 다음페이지로 넘어가면서 값들도 같이 넘어갈 수 있도록 구현하려고 합니다.
일반적으로 <form>
안에 있는 버튼을 클릭하거나 입력 필드에서 Enter 키를 입력하면 제출 이벤트가 발생합니다.
현재 구현된 Order
페이지에서는 Button
이 form
외부에 위치해 있습니다.
이를 해결하려면 <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
속성을 추가해 작동하지 않아 당황하였습니다ㅎㅎ
useRef를 사용하면 직접 DOM 요소에 접근하여 값을 가져올 수 있습니다.
form 요소에서 ref를 사용하면 onChange 없이도 입력값을 쉽게 조회 가능합니다. (불필요한 렌더링 방지)
const userNameRef = useRef(null);
const userTelRef = useRef(null);
//OrderForm.js
const handleSubmit = e => {
e.preventDefault();
const formData = {
name: userNameRef.current.value, //①ref를 통해 이름 입력값 가져오기
tel: userTelRef.current.value, // ②ref를 통해 전화번호 입력값 가져오기
payment: checked, // ⓷선택된 결제 수단 가져오기
};
onSubmit(formData); //④ 상위 컴포넌트로 데이터 전달
};
<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>
선택된 결제 수단 가져오기
→ Radio 버튼은 하나의 그룹 내에서 한번에 하나만 선택될 수 있기 때문에 current.value를 사용하지 않아도 선택된 값을 쉽게 확인 가능
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;