Form
HTML에서도 존재했던 폼이다. 마찬가지로 JSX코드를 통해 표준 JS로 변환된다.
이제는 버튼이 눌렸을 때, 이 폼(form)이 제출되도록 만들 것이다.
전반적인 코드 구성은 아래와 같다.
const [enteredTitle, setEnteredTitle] = useState("");
const [enteredAmount, setEnteredAmount] = useState("");
const [enteredDate, setEnteredDate] = useState("");
const titleChangeHandler = (event) => {
setEnteredTitle(event.target.value);
};
const amountChangeHandler = (event) => {
setEnteredAmount(event.target.value);
};
const dateChangeHandler = (event) => {
setEnteredDate(event.target.value);
};
const submitHandler = (event) => {
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date(enteredDate),
};
console.log(expenseData);
};
return (
<form onSubmit={submitHandler}>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input
type="text"
onChange={titleChangeHandler}
/>
</div>
<div className="new-expense__control">
<label>Amount</label>
<input
type="number"
min="0.01"
step="0.01"
onChange={amountChangeHandler}
/>
</div>
<div className="new-expense__control">
<label>Date</label>
<input
type="date"
min="2019-01-01"
max="2023-12-31"
onChange={dateChangeHandler}
/>
</div>
</div>
<div className="new-expense__actions">
<button type="submit">Add Expense</button>
</div>
</form>
);
<button type="submit">Add Expense</button>
에 바로 onClick={submitHeadler}
를 달고 싶지만 이 방법은 event를 수신하기 위한 좋은 방법은 아니다.
사실 브라우저에 내장된 기본 동작과 웹 페이지에 내장된 폼이 존재하기 때문에 만약 <button>
의 입력 타입이 "submit"이라면 <form>
대신에 submit하는 event를 수행한다.
따라서 <button>
에 직접적으로 event를 작성하는 것보다는 <form>
에 onSubmit={submitHeadler}
을 다는 방법을 채택했다.
하지만 또 다른 문제가 있으니, 버튼을 통해 폼이 제출되면 기본적인 브라우저의 동작으로 현재 페이지가 다시 로드된다.
브라우저는 폼이 제출될 때마다 이 웹페이지를 호스팅하고 있는 서버에 request를 보내기 때문이다.
대신에 JavaScript와 수동으로 데이터를 수집하고 결합하여 무언가를 제작하는 방법으로 폼을 변경하고 싶다.
다행이도 이런 브라우저의 기본 동작을 비활성화할 수 있는 방법이 있다.
event listener에 의해서 실행된 함수를 통해 event 객체를 받아서 preventDefault method를 호출할 수 있다.
const submitHandler = (event) => {
event.preventDefault() ;
} ;
해당 코드를 삽입하는 것만으로도 브라우저에서 기본 요청이 보내지는 것을 막을 수 있다.
그 요청이 보내지지 않기 때문에 페이지는 다시 로드되지 않을 것이고 JavaScript로 계속 이 페이지를 다룰 수 있게 되었다.
싱글 페이지 애플리케이션(Single Page Application, SPA)처럼 사용할 수 있을 것이다.
위 과정을 통해 결합된 enteredData를 출력할 수 있게 되었으며, 사용자 입력을 통해 데이터를 React로 수신받을 수 있었다.
Two-way Binding
그럼 입력한 여러 정보들을 버튼이 눌렸을 때 없어지게 할 수 있을까?
이런 이유로 state가 사용된다.
state를 사용하면 양방향 바인딩(two-way binding)이라는 것을 사용할 수 있다.
단어 뜻 그대로 변경되는 입력값만 수신하는 것이 아닌 입력에 새로운 값을 다시 전달할 수 있다.
상태를 업데이트하기 위해 입력에서 변경사항을 수신하는 것뿐만아닌 입력에 상태를 다시 보내주기도 하며, 변경된 상태에 따라 입력도 변한다.
그래서 프로그램에 따라 입력값을 재설정하거나 원하는 입력으로 변경할 수 있게 된다.
그 방법은 간단하다. 아래 코드를 보면서 이해해보자.
const [enteredTitle, setEnteredTitle] = useState("");
const [enteredAmount, setEnteredAmount] = useState("");
const [enteredDate, setEnteredDate] = useState("");
const titleChangeHandler = (event) => {
setEnteredTitle(event.target.value);
};
const amountChangeHandler = (event) => {
setEnteredAmount(event.target.value);
};
const dateChangeHandler = (event) => {
setEnteredDate(event.target.value);
};
const submitHandler = (event) => {
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date(enteredDate),
};
console.log(expenseData);
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
};
return (
<form onSubmit={submitHandler}>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input
type="text"
value={enteredTitle}
onChange={titleChangeHandler}
/>
</div>
<div className="new-expense__control">
<label>Amount</label>
<input
type="number"
value={enteredAmount}
min="0.01"
step="0.01"
onChange={amountChangeHandler}
/>
</div>
<div className="new-expense__control">
<label>Date</label>
<input
type="date"
value={enteredDate}
min="2019-01-01"
max="2023-12-31"
onChange={dateChangeHandler}
/>
</div>
</div>
<div className="new-expense__actions">
<button type="submit">Add Expense</button>
</div>
</form>
);
기존의 코드에서 무엇이 변했을까? 입력을 받은 <input>
에 value
property가 생긴 것을 볼 수 있다.
등록된 value
들은 각 state가 변화하면 마찬가지로 <input>
의 입력되어있던 값들을 override
해준다.
이에 submitHandler
에서 전부 초기화해주는 코드가 입력되어 기존에 입력되었던 state들이 ''로 초기화되는 것을 볼 수 있다.