Yeom Jae Seon·2021년 2월 8일
1

React공식문서 공부

목록 보기
8/11
post-thumbnail


HTML의 form 엘리먼트(React 엘리먼트와 다르다..)는 자체가 내부 상태를 가지기 때문에, 다른 Dom 엘리먼트들과 다르게 동작한다.

예를들면

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

위 HTML의 form엘리먼트는 input에 들어가는 value를 name에 넣어서 다른 페이지로 이동하는 기본적인 동작을 행한다.

React에서 폼을 다룰때 이런방식으로 폼을 사용하고싶으면 사용해도 되지만 대부분의 경우 JS함수로 폼의 submit을 관리하고 사용자가 form에 입력한 데이터에 접근 가능하도록 하는것이 편하고 안전하다.
이를 위한 방식은 제어 컴포넌트(controlled componenets)라고 하는 기술을 이용하는 것이다.

  • HTML의 Form element는 기본적으로 동작하는 방식이 다른 DOM element들과 다르다.
  • React에서는 제어컴포넌트방식을 이용해서 Form을 관리할수 있다.
  • 제어컴포넌트방식을 이용하면 JS로 폼 submit을 관리할수 있고 사용자가 form에 입력하는 데이터에 접근 할수 있다

제어 컴포넌트(Controlled Componenet)


위에서 말했듯이 HTML Form의 <input>, <textarea>, <select>같은 폼엘리먼트들은 사용자의 입력 데이터를 자신들이 알아서 관리하고 업데이트한다.
반면에 React에서는 변경가능한 데이터를 컴포넌트의 state로 유지되며 setState에 의해 업데이트된다. -> re rendering

우리는 React state를 "신뢰 가능한 단일 출처"로 만들어 두 요소를 결합할수 있다. 그렇게 되면 폼을 렌더링하는 React컴포넌트는 폼에 입력되는 사용자의 입력값을 제어한다. 이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를 제어 컴포넌트라고 한다.

즉, 폼을 React의 state로 관리하면 폼에 입력되는 사용자의 입력값을 React가 관리할수 있고 그러한 폼 엘리먼트를 제어 컴포넌트라고 한다.(HTML 폼 엘리먼트는 기본적으로 자기 자신이 사용자의 입력값을 관리하고 업데이트하며 submit이되면 다른 주소로 name의 속성값에 value를 담아서 전달한다. 이렇게 되면 React가 폼에 입력되는 입력값을 관리할수 없고 JS로 폼 데이터 제출이 이루어지지 않으므로 편하지도않고 안전성도 떨어진다. 그래서 제어컴포넌트를 사용하는 것이다.)

바로 예시를 보면

import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: "" };
  }
  onSubmitHandler = (e) => {
    e.preventDefault();
    alert(`${this.state.value} 전송 완료`);
  };

  onChangeHandler = (e) => {
    this.setState({ value: e.target.value });
  };
  render() {
    return (
      <form onSubmit={this.onSubmitHandler}>
        <label>
          이름 :
          <input
            type="text"
            value={this.state.value}
            onChange={this.onChangeHandler}
          />
        </label>
        <input type="submit" value="제출" />
      </form>
    );
  }
}

export default App;

이렇게 input의 value를 state로 관리하고 onChange 이벤트에 의해 setState를 발생시켜서 input value와 React의 state값을 동일하게 하고있다.
즉 항상 input의 value는 this.state.value와 똑같게 되고 React state는 신뢰가능한 단일 출처가된다.

좀더 구체적인동작은 setState에의해 리렌더링이 발생한다를 생각하면 어렵지않다.

이젠 HTML 폼 엘리먼트가 제어 컴포넌트가 되어서 React의 state에 의해 관리가 가능하게 되었다.
이렇게 되면 조금 작성하는데 귀찮아 지긴했지만 다른 UI 엘리먼트에 input의 값(제어컴포넌트로 React state로 관리되닌까 이건 이제 state겠지?)을 전달하기도 편하고 다른 이벤트 핸들러에서 값도 재설정할수 있고 굉장히 관리가 용이해졌다.

  • HTML 폼 엘리먼트를 React State로 관리하면 React state에서 사용자가 폼에 입력하는 값을 관리할수 있게된다. 이젠 이 폼을 제어 컴포넌트(controlled componenet)라고 한다.
  • 제어컴포넌트가 되면 편리한 것이 사용자가 폼에 입력하는 데이터를 React state로 관리하기 때문에 관리하기가 훨씬 용이해졌다는 것이다.

textarea 태그


위 예시에선 폼엘리먼트중 input태그의 value를 React state로 관리하고 onChange이벤트에 setState하는 함수를 통해 input의 value를 React state로 완전히 관리했다.
즉, 제어컴포넌트를 만들었다.

폼엘리먼트중 하나인 textarea도 동일하다.

import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: "" };
  }
  onSubmitHandler = (e) => {
    e.preventDefault();
    alert(`${this.state.value} 전송 완료`);
  };

  onChangeHandler = (e) => {
    this.setState({ value: e.target.value });
  };
  render() {
    return (
      <form onSubmit={this.onSubmitHandler}>
        <label>
          이름 :
          <textarea
            type="text"
            value={this.state.value}
            onChange={this.onChangeHandler}
          />
        </label>
        <input type="submit" value="제출" />
      </form>
    );
  }
}

export default App;

textarea도 value를 React state로 관리하였고 onChange될때마다 setState로 state를 업데이트하여 value도 변경하였다. -> 완전히 React state로 textarea를 관리하엿다. -> 제어컴포넌트가 되었다.

실제로 예시 바꾼것도 input -> textarea밖에 없다.

  • textarea 폼 엘리먼트도 제어컴포넌트로 바꾸는 과정은 input폼엘리먼트와 동일하다.

select 태그


select 폼엘리먼트를 제어컴포넌트로 만들어보자.
일단 HTML에선 selected속성으로 옵션을 초기화한다.

    <select>
      <option value="grapefruit">Grapefruit</option>
      <option value="lime">Lime</option>
      <option value="coconut">Coconut</option>
      <option selected value="mango">Mango</option>
    </select>

망고가 초기화 되었다.

React에서는 제어컴포넌트를 사용할때 이 selected속성을 사용하는 대신 다른 폼엘리먼트와 동일하게 최상단 select태그에 value를 사용하여 관리한다.
중구난방으로 option태그에 selected속성을 부여하는것보단 훨씬 간단하다.

import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: "mango" };
  }
  onSubmitHandler = (e) => {
    e.preventDefault();
    alert(`${this.state.value} 구입 완료`);
  };

  onChangeHandler = (e) => {
    this.setState({ value: e.target.value });
  };
  render() {
    return (
      <form onSubmit={this.onSubmitHandler}>
        <label>
          과일 뭐사지? :
          <select value={this.state.value} onChange={this.onChangeHandler}>
            <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="구매" />
      </form>
    );
  }
}

export default App;

input, textarea와 동일하게 value를 state로 관리하고 onChange될때마다 setState로 state업데이트 === value 업데이트를 통해 제어컴포넌트를 만들었다.
별로 안어렵다.

  • select태그도 사용자의 입력값을 React state로 관리하는 방법으론 (제어 컴포넌트로 만드는 방법으론) value에 state를 넣고 onChange될때마다 setState시킨다.

file input태그


이젠 제어컴포넌트를 사용할수 없는 폼 엘리먼트에 대해서 알아보자.

지금까진 사용자가 단순히 폼엘리먼트에 입력되는 값을 state로 관리해서 제어컴포넌트를 만들어 사용했다.

그런데 <input type="file" />같은 폼엘리먼트의 경우엔 로컬 pc의 하나이상의 파일을 서버에 업로드하는등의 동작을 할때 사용된다.
즉, 이 폼엘리먼트에 입력되는 값은 조작할수있는게아니라 입력되는 파일을 읽기만 하므로 읽기 전용이다.
그래서 이러한 폼엘리먼트를 React에서는 비제어 컴포넌트라고 부른다.
제어 컴포넌트완 다르게 제어할수 없는 읽기전용이므로 비제어 컴포넌트라고 부르는 것이다.

  • 제어할수 없는 사용자의 입력값, 즉 읽기전용인 사용자의 입력에 대해선 State로 관리할수 없기 때문에 이러한 폼엘리먼트는 비제어 컴포넌트라고 한다.

다중 입력 제어하기


React에서 여러 input, select, textarea 엘리먼트를 제어컴포넌트로 만들어 관리할때 각 엘리먼트에 name 속성을 추가하여 e.target.name을 이용해서 사용자의 입력값을 제어할수 있음
바로 예시로 들어가자.

import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { name: "", age: "", isExpereience: false };
  }
  onSubmitHandler = (e) => {
    e.preventDefault();
    alert("제출 완료");
  };

  onChangeHandler = (e) => {
    const name = e.target.name;
    const value = e.target.value;
    this.setState({ [name]: value });
  };
  render() {
    return (
      <form onSubmit={this.onSubmitHandler}>
        <label>
          이름입력:
          <input
            type="text"
            value={this.state.name}
            onChange={this.onChangeHandler}
            name="name"
          />
        </label>
        <br />
        <label>
          나이 입력:
          <input
            type="number"
            value={this.state.age}
            onChange={this.onChangeHandler}
            name="age"
          />
        </label>
        <br />
        <label>
          운동경력 있나요?
          <input
            type="checkbox"
            value={this.state.isExpereience}
            onChange={this.onChangeHandler}
            name="isExpereience"
          />
        </label>
        <br />
        <input type="submit" value="제출" />
      </form>
    );
  }
}

export default App;

이렇게 각 폼 엘리먼트에 name속성을 이용해서 onChange가 일어날때마다 적절하게 setState로 state를 업데이트 하고있다.

여기서 알아야할 두가지는
1. setState의 state는 병합된다는 사실
2. object key에 접근하는 [] computed property name구문인 JS문법
이다.
그래야 이해가간다.

  • 다중 입력 폼엘리먼트를 제어컴포넌트로 관리하기 위해선 name속성을 이용한다.

제어되는 Input Null


제어컴포넌트로 만들면 개발자가 의도한게 아니라면 input의 value를 사용자가 변경할수 없다.
그치만 null을 value에 넣으면 사용자가 변경할수 있다.

import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");
ReactDOM.render(<input type="text" value="hi" />, rootElement);

아무리 뭐입력해도 입력할수 없지만

import ReactDOM from "react-dom";

const rootElement = document.getElementById("root");
ReactDOM.render(<input type="text" value={null} />, rootElement);

null을 value에 넣으면 입력가능해진다.

  • input value에 null을 넣으면 사용자가 value를 변경가능하다.
  • 제어컴포넌트로, React state로 폼엘리먼트 관리할때 이런식으로 null을 value에, 즉 state에 넣으면 다양한 활용이 가능하겠다.

제어컴포넌트의 대안


폼 내부의 데이터관리를 모두 React State로해야하고 이벤트 핸들러를 통해서 사용자의 데이터를 React state와 동일화 시켜 관리하는건 귀찮을 수도있다.
이러한 경우의 대안으로 비제어 컴포넌트가 존재한다.

잠깐 맛만보자면 ref를 이용하는 것이다.

import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }
  onSubmitHandler = (e) => {
    e.preventDefault();
    alert(`${this.inputRef.current.value} 전송 완료`);
  };

  render() {
    return (
      <form onSubmit={this.onSubmitHandler}>
        <label>
          이름 :
          <input type="text" ref={this.inputRef} />
        </label>
        <input type="submit" value="제출" />
      </form>
    );
  }
}

export default App;

이렇게 다른 DOM 엘리먼트에 접근할수 있는 (JS에서의 getElementById라던가 querySelector의 React 방식) ref를 이용해서 폼을 관리할수있다.
대신 비제어 컴포넌트가되어 사용자가 폼엘리먼트에 입력하는 값을 관리할수 없으므로 제어 컴포넌트보다 좋다고 하긴 힘들겠다.

  • React에서 폼엘리먼트 관리하는 방법으론 제어컴포넌트말고도 ref를 이용하는 비제어 컴포넌트가 존재한다.

위 내용은 공식문서를 보고 공부한 내용입니다.
https://ko.reactjs.org/docs/forms.html

0개의 댓글