HTML 폼 요소는 자체가 내부 상태를 가지기 때문에 React의 다른 요소들과 다르게 동작한다. 예를 들어, 순수한 HTML에서 이 폼은 name을 입력받는다.

<form>
  <label>
    Name:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="Submit" />
</form>

이 폼을 제출하면 새로운 페이지로 이동하는 기본 동작을 한다. React에서도 같은 동작을 원한다면 그대로 사용하면 되지만 javascript를 통해 제출을 처리하는 것이 편리하다. 이를 제어 컴포넌트라고 한다.


제어 컴포넌트(Controlled Component)

React state를 “신뢰 가능한 단일 출처"로 만들어 두 요소를 결합한다. 그러면 폼을 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어한다. 이를 제어 컴포넌트라고 한다. 예를 들어 이전 예시가 전송될 때 이름을 기록하길 원한다면 제어 컴포넌트로 작성할 수 있다.

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 attribute는 폼 엘리먼트에 설정되므로 표시 값은 항상 this.state.value 값이 되고 React state는 신뢰 가능한 단윌 출처(single source of truth)가 된다. React state를 업데이트하기 위해 모든 키 입력에서 handleChange가 동작하기 때문에 사용자가 입력할 때 보여지는 값이 업데이트 된다.

제어 컴포넌트를 사용하면, input의 값은 항상 React state에 의해 결정된다. 하지만 이 값을 다른 컴포넌트에 넘길 수 있기 때문에 의미가 있다.

textarea 태그

textarea태그는 원래 자식을 갖지만, React에서는 input처럼 value attribute를 대신 사용한다.

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>
    );
  }
}

Select 태그

HTML에서는 selected 어트리뷰트를 이용해 초기값을 설정하지만, React에서는 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>
		);
	}
}

⚠️ 주의 select 태그에 multiple 옵션을 허용한다면 value 어트리뷰트에 배열을 전달할 수 있다. <select multiple={true} value={['B', 'C']}>****


File Input 태그

javascript의 File API를 통해 조작할 수 있다. 하지만 값이 읽기 전용이기 때문에 비제어 컴포넌트이다.