- 이 글은 React 공식 홈페이지 Docs v16.8.3 (https://reactjs.org/docs) 에 기반하여 작성한 글입니다.
- Forms
HTML form element 는 다른 DOM element 와 다르게 동작한다. 우리가 form submit 을 써봤으면 알 수 있듯이 내부의 상태(state) 를 가지면서 동작하기 때문이다.
HTML 에서 기본적으로 form 태그를 이용해서 submit 을 하면 다른 페이지로 이동하게 되어있다. 물론 React 에서도 원하면 똑같이 할 수 있다.
하지만 대부분의 경우에는 submit 을 다루는 javascript 함수를 정의하고 User 가 form 태그안에 들어왔을 때 data 에 접근하는 용도로 사용한다. 일반적으로 이런방식을 "controlled components" 라고 칭한다.
- Controlled Components
HTML 에서 <input>, <textarea>, <select> 같은 form element 들은 상태를 유지하거나 유저의 input 에 따라서 값을 갱신한다. React 에서는 state 로 상태 전이를 표현하고 setState() 함수를 통해서만 갱신한다.
React 의 state 를 단일 소스 저장소("SSOT - single source of truth") 로 만들어서 위의 두 가지 특성을 무리없이 소화할 수 있다. (form element 들이 값을 표시할 때 React의 state를 참조하고, user 가 입력을 할 때에도 React의 state 를 update 하여 data의 sync가 맞는다는 소리 인 것 같다.)
그리고 또 form 을 rendering 하는 React component 는 user 의 input 이후에 일어나는 일들을 제어한다. 이런 방법으로 React 에 의해 제어되는 input form element 를 "controlled component" 라고 부른다.
예제를 한번 보자. 만약 위에서 정의한 <form> 태그가 submit 될 때 name 을 로그로 남기고 싶다면 form 을 controlled component 로 작성할 수 있다.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event){
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" name="name" value={this.state.value}
onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
form element 에 value attribute 값이 세팅되었기 때문에 보여지는 값은 언제나 this.state.value 를 참조한다. (React state 가 소스 저장소가 되었다.)
handleChange 는 모든 키보드 입력마다 React의 state 를 갱신하여서 보여지는 값은 유저 입력에 따라서 갱신된다. 시간순으로 보면 아래와 같은 동작이 일어날 것이다.
유저입력 -> handleChange -> React 의 state 갱신 -> form element 가 React state를 참조
controlled component 에서 모든 state 의 변화는 handler 함수와 연관이 있는데 state 를 갱신하거나 user의 입력을 검증할 수도 있다.
예를 들어 우리가 입력을 강제로 대문자로 변경하길 원한다면 handleChange 함수를 변경할 수 있다.
- The textarea Tag
HTML 에서 <textarea> element 는 value 값을 세팅하지 않고 자식으로 text 를 참조하는 형태이다.
React 에서는 <textarea> 에 value attribute 를 이용할 수 있다. <textarea> 를 한줄로 사용하는 input form 들과 비슷한 문법으로 사용할 수 있게 해준다.
class EssayForm extends React.Component{
constructor(props){
super(props);
this.state = {
value: 'Please input text'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('essay is ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<textarea value={this.state.value}
onChange={this.handleChange} />
<input type="submit" value="Submit"/>
</form>
);
}
}
ReactDOM.render(
<EssayForm />,
document.getElementById('root')
);
생성자에서 value 값은 초기화했기 때문에 처음 app 이 구동되자마자 해당문자열이 보일 것이다.
- The select Tag
HTML 에서 <select> 태그는 drop-down 목록를 만든다. 문법예를 한번 살펴보자.
drop-down 목록중에 coconut 이 초기 선택값임을 알 수 있다.(selected attribute)
React 에서는 selected attribute 대신에 select 태그 레벨에 value attribute 를 사용해서 선택된 값을 표현한다. 이런 문법은controlled component 사용을 더 편리하게 해주는데 그 이유는 update 할 장소를 한곳으로 수렴시켜 주기 때문이다. select 태그를 이용한 예제를 한번 구성해보자.
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'coconut'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('selected value is ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<select value={this.state.value}
onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
<input type="submit" value="Submit"/>
</form>
);
}
}
ReactDOM.render(
<FlavorForm />,
document.getElementById('root')
);
React는 <input type="text>, <textarea>, <select> 와 같이 태그가 달라져도 value attribute 만을 이용해서 controlled component 를 구현할 수 있게 해준다.
그리고 <select> 태그 같은 경우 value attribute 에 multiple 값을 줄 경우 값을 가지고 있다. 아래 예제는 아무런 동작도 하지 않고 Submit 버튼을 곧바로 누른 경우인데 UI 를 표현하는 코딩은 하지 않았지만 alert 에 값들이 있는것을 확인할 수 있다.
- The file input Tag
HTML 에서 <input type="file"> 태그는 User 가 1개 이상의 파일을 서버에 업로드 하기 위해 특정 Device 에서 선택할 수 있게 도와준다.
file input 의 value 는 read-only 이기 때문에 React 에서는 uncontrolled component 로 취급한다.
- Handling Multiple Inputs
여러개의 controlled input element 가 필요할 때 각 element 에 name attribute 를 추가하면, 같은 handler function을 이용하더라도 event.target.name 값을 기반으로 제어할 수 있게 된다.
isGoing 과 numberOfGuests 라는 이름을 가진 input element 2개를 다루어 보자.
class Reservation extends React.Component {
constructor(props){
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is Going:
<input name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number Of Guests:
<input name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
)
}
}
ReactDOM.render(
<Reservation />,
document.getElementById('root')
);
위에서 handleInputChange 함수 안에서 this.setState({[name]: value}) 로 변경하였는데 이는 ES6 computed property name 문법을 이용한 것이며, ES5로 동치문법은 아래와 같다.
알고 있듯이 setState() 함수가 자동으로 현재의 this.state 와 병합하기 때문에 값이 변하는 부분에서 단순히 setState를 호출하기만 하면 된다.
- Controlled Input Null Value
controlled component 에서 value attribute 에 값을 지정하면 user 가 input 값을 변경하는걸 막아준다. 만약 value 값을 특정지었지만 input 값이 수정가능한 상태가 되어야 한다면 value 을 undefined 나 null 로 설정하면 수정가능하게 할 수 있다.
아래코드를 보자.
처음에는 hi 에서 키보드로 입력을 하여도 값이 변하지 않지만 1초 후에는 수정가능한 상태가 된다.
- Alternatives to Controlled Components
때로는 controlled components 를 사용하는게 지루한 작업이 될 수도 있다. 모든 data 가 변할 수 있는 곳에 하나하나 event handler 를 정의해야하고, React Component 의 state 값도 동기화 시켜야 한다.
만약 기존에 작성된 코드가 있고 이걸 React 로 변경시켜야 하거나, React 라이브러리가 아닌걸 React 와 병합해야 한다면 꽤나 성가신 작업이 되게 된다.
이런 상황에 처하게 되면 input form 들을 구현하기 위해서 대안으로 uncontrolled components 를 사용해 보길 원할 수도 있겠다. (uncontrolled components 참조 : https://reactjs.org/docs/uncontrolled-components.html)
- Fully-Fledged Solutions
공식 문서에서는 validation, form submit handling, field 에 대한 정보를 모두 추적해주는 솔루션을 찾고 있다면 Formik 를 사용해 보라고 추천하고 있다.
Formik link : https://jaredpalmer.com/formik/
하지만 controlled component 와 동일한 원리를 적용하고 있고 state 를 관리한다는 점은 똑같으므로 러닝커브 혹은 귀찮음에 대해 부정적으로 보지 말라는 당부도 하고 있다.
'Language > React' 카테고리의 다른 글
[React 공식 Doc 가이드 #12] Thinking in React (0) | 2021.02.11 |
---|---|
[React 공식 Doc 가이드 #11] Lifting State Up (0) | 2021.02.11 |
[React 공식 Doc 가이드 #9] Lists and Keys (0) | 2021.02.11 |
[React 공식 Doc 가이드 #8] Conditional Rendering (0) | 2021.02.11 |
[React 공식 Doc 가이드 #7] Handling events (0) | 2021.02.11 |
댓글