하나씩 알아가기

리액트에서의 Form(제어 컴포넌트와 비제어 컴포넌트) 본문

리액트

리액트에서의 Form(제어 컴포넌트와 비제어 컴포넌트)

clearwater 2021. 2. 4. 17:07
728x90
반응형

틀린 부분이 있을 경우, 지적해 주시면 감사하겠습니다.

 

<form> 태그를 사용하여 사용자로부터 정보를 입력받아 서버에 요청할 수 있습니다.

HTML의 폼 엘리먼트는 그 자체가 내부 상태를 가집니다.

리액트에서는 이를 그대로 사용하기 보다 JavaScript 함수로 폼의 제출을 처리하고 사용자가 폼에 입력한 데이터에 접근하도록 합니다.

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

HTML 폼을 이용하려면 <input>태그의 name이라는 속성에 담아야 제출할 수 있습니다.

하지만 이렇게 리액트 앱을 구현하는 경우 HTML 폼을 이용하는 경우는 많지 않다고 생각됩니다(왜냐하면 리액트는 CSR방식의 렌더링을 지향하기 때문에 하단 포스팅 참고).

clearwater92.tistory.com/30

 

서버사이드 렌더링(SSR)과 클라이언트 사이드 렌더링(CSR)

틀린 부분이 있을 경우, 지적해 주시면 감사하겠습니다. SSR과 CSR을 비교하여 정리해 보겠습니다. SSR(Server-Side Rendering) 전통적인 웹 어플리케이션 방식으로, 브라우저가 서버에 요청을 하고 서버

clearwater92.tistory.com

 

제어 컴포넌트(Controlled Component)

리액트의 state에 의해 값이 관리, 변경됩니다.

리액트 공식 문서에 있는 제어 컴포넌트의 예제를 살펴보겠습니다.

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

리액트에서 input 처리의 흐름은 다음과 같습니다

  1. NameForm 생성자 호출 시, state를 만듭니다(신뢰 가능한 단일 출처)

  2. 사용자가 텍스트박스에 입력을 하면 키보드 이벤트로 state 값이 갱신되고

  3. value 속성이 갱신된 state값을 가져오고 UI가 갱신됩니다

  4. 이 상태에서 enter를 치면 onSubmit에 의해 텍스트박스 안에 있는 값이 handleSubmit에 전달되고 alert를 발생시킵니다.

form에서 submit이 발생할 때 새로고침(페이지를 다시 불러옴)이 이루어 지는 것이 기본 동작인데 이 동작을 막기 위해서 event.preventDefault를 호출하였습니다.

 

textarea 태그

textarea 태그는 input 태그와 사용법은 동일합니다. 다만 차이점은

input태그의 경우 value 속성에 입력 값이 들어가 있지만

textarea태그는 입력한 텍스트를 자식으로 정의한다는 차이가 있습니다.

 

select 태그

드롭다운 목록을 만드는 <select> 태그입니다. 스타일만 다르지 제어 컴포넌트 방식이기 동작하는 방식은 <input>, <textarea>태그와 동일합니다. 다만 selected 키워드를 넣어준 option이 초깃값이 된다는 것을 알아야 합니다.

 

다중 입력 제어하기

여러 input 엘리먼트를 제어하는 경우에는 각 엘리먼트에 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>
    );
  }
}

위 예시에서는 input으로 체크박스와 number를 다루고 있습니다. setState로 각각의 value를 제어하는데 

  • checkbox -> target.checked
  • number -> target.value

타입에 따라 value를 따로 처리하는 것을 확인할 수 있습니다.

 

순서를 나타내보면 다음과 같습니다

사용자 입력 -> onChange -> handleInputChange -> target.type이 무엇인지 ?(삼항연산자) -> 제어에 따른 value 값을 얻음 -> name에 따라 value를 쌍으로(pair) state를 갱신

computed property name 속성을 이용하여 name에 따라 다른 value를 state에서 제어하는데 이 ES6 문법은 비구조화할당, 스프레드 연산자(...)와 더불어서 리액트에서 자주 사용되므로 꼭 숙지해 두는 것이 좋습니다.

var partialState = {};
partialState[name] = value;
this.setState(partialState);

ES5 문법

this.setState({
  [name]: value
});

ES6 문법(computed property name)

 

 

 비제어 컴포넌트(Uncontrolled Components)

제어컴포넌트와 반대되는 개념입니다. 비제어컴포넌트에서는 DOM 자체에서 폼 데이터가 다루어집니다.

 

state에 대한 업데이트 이벤트 핸들러를 작성하는 대신 Ref를 사용하는데, 이를 통해 DOM에서 폼 값을 가져올 수 있습니다.

Ref를 사용해야 할 때

  • 포커스, 텍스트 선택영역, 혹은 미디어의 재생을 관리할 때

  • 애니메이션을 직접적으로 실행시킬 때

  • 서드 파티 DOM 라이브러리를 React와 같이 사용할 때

import React from 'react';

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

React.createRef()로 Ref 객체를 생성하고 해당 React 엘리먼트의 ref 속성에 이 값을 넣어주면 참조하여 접근할 수 있게 됩니다. 텍스트 박스에 "apple"을 입력하고 엔터를 치면 

handleSubmit 이 발생한 이벤트를 받고 this.input.current.value 에 우리가 입력한 "apple"이 담긴 것을 확인할 수 있습니다.

함수형 컴포넌트에서 Ref로 특정 DOM을 선택하려면 useRef hook을 사용해야 합니다.

 

파일 입력 태그

class FileInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.fileInput = React.createRef();
  }
  handleSubmit(event) {
    event.preventDefault();
    alert(
      `Selected file - ${this.fileInput.current.files[0].name}`
    );
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Upload file:
          <input type="file" ref={this.fileInput} />
        </label>
        <br />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

export default FileInput;

파일을 입력받을 때도 비제어 컴포넌트를 사용합니다. 파일을 다루려면 Web API(리액트 말고 외부 라이브러리)와 상호작용해야 하기 때문인 것 같습니다.

HTML 기본 엘리먼트를 통해 "file.txt"를 선택하였습니다. Submit 버튼을 누르면 this.fileInput.current를 통해 file 객체를 확인할 수 있습니다.

 

제어되는 Input Null 값

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 3000);

input 값에 value가 prop으로 지정되어 있기 때문에 변경할 수 없지만, 타이머로 1초 뒤에 렌더링이 되도록 하고 value를 null로 설정하도록 하면, 3초 뒤에 입력이 가능해 지는 것을 확인해 볼 수 있습니다.

 

참고 및 출처

ko.reactjs.org/docs/forms.html

 

폼 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

ko.reactjs.org/docs/uncontrolled-components.html#the-file-input-tag

 

비제어 컴포넌트 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

blog.naver.com/futureds/222078018357

 

제어(Controlled) 컴포넌트와 비제어(Uncontrolled) 컴포넌트

리액트로 특정 HTML 요소(대부분 form의 input, select, textarea 등)를 이용할 때 사용되는 개념이 제...

blog.naver.com

ko.reactjs.org/docs/refs-and-the-dom.html

 

Ref와 DOM – React

A JavaScript library for building user interfaces

ko.reactjs.org

simfatic.com/forms/help/v50/why_do_i_need_to_upload_form_t_2_2.html

 

Why do I need to upload form to a webserver?| Simfatic Forms 5.0 Help

A form has two parts: the client side HTML form  and a server side form processor script. The client side displays the form and takes the input whereas the server side does the form processing (send email, save form submissions etc). In addition to that,

simfatic.com

 

728x90
반응형