HTML 폼 요소는 자체 내부 상태를 갖고 있기 때문에, 폼은 React에서 다른 DOM 요소와 조금 다르게 작동합니다. 예를 들어, 순수한 HTML에서 이 폼은 이름을 받습니다.
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
이 폼에는 사용자가 폼을 제출할 때 새 페이지로 이동되는 기본 작동을 합니다. 만약 React에서 동일한 동작을 원한다면, 그냥 동작 하도록할 수 합니다. 그러나 대부분의 경우 form 제출을 처리하고 사용자가 form에 입력한 데이터에 접근할 수 있게하는 자바스크립트 함수를 가지는 게 편합니다.
이 동작을 위한 표준 방식은 “제어되는 컴포넌트 (Controlled Components)”
기법을 사용하는 것입니다.
HTML에서, 폼 등의 요소 <input>
, <textarea>
, <select>
는 일반적으로 자신의 상태를 유지하고 사용자 입력을 기반으로 업데이트합니다. React에서, 변경 가능한 state
는 일반적으로 컴포넌트의 state
속성에 존재하며, setState()
로만 업데이트할 수 있습니다.
React state
를 신뢰 가능한 단일 소스 (single source of truth) 로 만들어 두 요소를 결합할 수 있습니다. 그런 다음 렌더링 되는 React 컴포넌트는 이후에 폼에서 발생하는 유저 입력을 제어합니다. 이런 방식으로 React에 의해 제어되는 Input
폼 요소는 “제어되는 컴포넌트”
라고 부릅니다.
예를 들어, 이전 예제가 submit 될 때 이름을 기록하게하려면, 제어 된 구성 요소로 양식을 작성할 수 있습니다.
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" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
value
속성은 폼 요소에 설정되므로 표시되는 값은 항상 this.state.value
가 되고 React state가 신뢰 가능한 소스가 됩니다. React state를 업데이트하기 위해 모든 키 이벤트에서 handleChange
가 동작하기 때문에 사용자가 입력할 때 표시되는 값이 업데이트됩니다.
제어되는 컴포넌트를 사용하면 모든 state
변경과 연관되는 핸들러 함수가 생깁니다. 이를 통해 사용자 입력을 수정하거나 검증하는 것이 간단해집니다. 예를 들어 모든 유저의 이름을 강제로 대문자로 받고싶다면 handleChange
를 다음과 같이 쓸 수 있습니다.
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
HTML에서, <textarea>
요소는 자식으로 값을 정의합니다.
<textarea>
Hello there, this is some text in a text area
</textarea>
React에서 <textarea>
는 자식값이 아닌value
속성을 사용합니다. 이렇게 하면 <textarea>
를 사용하는 폼은 한 줄 입력을 사용하는 폼과 매우 유사하게 작성할 수 있습니다.
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Essay:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
this.state.value
를 constructor
에서 설정하기 때문에 textarea
는 값을 갖고 시작 할 수 있습니다.
HTML에서, <select>
는 드롭 다운 목록을 만듭니다. 예를 들어, 이 HTML은 과일 드롭 다운 목록을 만듭니다.
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
Coconut
옵션에 selected
속성이 있기 때문에 기본적으로 선택되는 걸 주목합시다. React에서는 selected
속성을 사용하는 대신 루트 select
태그에 value
속성을 사용합니다. 한 곳에서 업데이트만 하면 되기 때문에 제어되는 컴포넌트에서 사용하기 더 편합니다. 예를 들어,
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('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite flavor:
<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>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
이렇게 한다면 <input type="text">
, <textarea>
, <select>
는 모두 유사하게 작동합니다. 이것들은 제어되는 컴포넌트를 구현할때 value
속성을 사용할 수 있습니다.
노트
select
태그에서 여러 옵션을 선택하고 싶다면 value속성에 배열을 전달합니다.
HTML에서는 <input type="file">
을 사용하여 하나의 파일을 선택하여 서버에 업로드 하거나 File API 를 통해 JavaScript로 조작 할 수 있습니다 .
<input type="file" />
input[type="file"]
의 값은 읽기 전용이기 때문에, React 에서 uncontrolled(제어되지않는) 구성요소 입니다.
문서의 뒷부분에서 제어되지 않는 구성요소와 함께 논의 합니다.
여러개의 제어되는 구성요소를 처리하고싶은 경우 name
속성을 추가한 후, event.target.name
을 이용합니다.
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>
);
}
}
주어진 name
에 해당하는 state
키를 업데이트 하기 위해 ES6 Computed property name(계산 된 속성)응 사용할 수 있습니다.
this.setState({
[name]: value
});
이 코드를 아래 ES5 코드와 같습니다.
var partialState = {};
partialState[name] = value;
this.setState(partialState);
name
을 state
의 속성 키 로써 setState()
를 통해 state
를 병합되므로 변경된 부분만 적용됩니다.
제어되는 컴포넌트에 value
값으로 prop
값을 지정하면 사용자는 입력을 변경할 수 없습니다.
value
는 지정했지만 입력하게 하고 싶다면, value
값을 null
또는 undefined
로 지정하면 됩니다.
아래 코드는 처음에는 수정이 불가능하지만 잠시후 편집이 가능하게 됩니다.
ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
데이터를 변경하는것들 마다 이벤트 핸들러를 작성하고 React 컴포넌트에서 모든 input state를 파이프해야하기 때문에 제어되는 컴포넌트를 사용하는 것은 지루할 수 있습니다.
기존 코드베이스를 React로 변환하거나 React 애플리케이션을 React 라이브러리가 아닌 것들을 통합하고 싶을때 귀찮은 일이 될 수 있습니다. 이런 상황에서는 대체기술은 제어되지않는 구성요소를 사용할 수 있습니다.
유효성 검사, 방문한 필드 추적 및 form submit
처리와 같은 완벽한 솔루션이 필요하다면 Formik는 많은 사람들이 사용하는것 중 하나입니다. 그러나 이것은 제어되는 컴포넌트와 state
관리의 동일한 원칙에 따라 작성되었으므로 이를 배우려는 것을 게을리하지 마십시오.