본문 바로가기

React/실험실

[React] Controlled and UnControlled Input

※ 본 글은 React의 제어 및 비제어 양식 입력이 복잡할 필요는 없습니다 내용을 정리한 것입니다.

해석 및 개인적인 의견을 추가한 글입니다.

 

다소 오역이 있을 수 있습니다.... FBI WARNING.....

 

React에서의 Controlled와 UnControlled Input

[출처] 원글 - Controlled 인풋은 값이 없고 UnControlled 인풋은 값이 존재한다.

많은 아티클에서 "너는 useState를 사용해서는 안 된다"라고 말합니다.

또한 문서에서 "Ref는 나쁘다"라고 하고 있습니다.


  작성자

작성자는 Ref를 사용하는 것이 나쁘다는 이야기를 특별히 들어본 적이 없습니다. 그래서 찾아보니

Don’t Overuse Refs

Your first inclination may be to use refs to “make things happen” in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to “own” that state is at a higher level in the hierarchy. See the Lifting State Up guide for examples of this.

 

"일을 처리" 하기 위해서 ref를 꼭 사용해야 하는지, 아니면 해당 데이터나 로직을 컴포넌트의 상태나 props를 통해

관리할 수 있는지 고려해 보라고 하고 있다. 

 

즉, 필요성을 한번 더 생각해보라는 의미인듯하다.


이러한 기준은 매우 모순적이다. 선택의 기준이 무엇인지도 이해하기 어려운 상황이다. 

그렇다면 어떻게 우리는 Form을 만들어야 할까? 

 

Form은 많은 웹 앱에서 핵심이 된다. 그럼에도 리액트에서의 Form 처리는 약간,,, 구석에 있는 것 같다. 

 

이 글을 통해서 각 접근 방식 간의 차이점과 접근 방식을 언제 사용해야 할지 알아보자.

The UnControlled

UnControlled Input은 기존 HTML 입력 폼과 같이 사용할 수 있다 : 

class Form extends Component {
  render() {
    return (
      <div>
        <input type="text" />
      </div>
    );
  }
}

 

입력폼은 우리가 입력한 값을 기억하고 있다. 그리고 그 값을 ref를 통해서 사용할 수 있다. 

예를 들어 onClick 핸들러를 가지고 있는 버튼처럼 : 

class Form extends Component {
  handleSubmitClick = () => {
    const name = this._name.value;
    // do something with `name`
  };

  render() {
    return (
      <div>
        <input type="text" ref={(input) => (this._name = input)} />
        <button onClick={this.handleSubmitClick}>Sign up</button>
      </div>
    );
  }
}

 

즉, 필요할 때 필드에서 값을 "가져와야 ( Pull )" 한다. 이것은 보통 양식이 제출될 때 발생할 수 있다. 

 

이 방법은 Form을 구현하는 가장 간단한 방법이고 React를 배울 때 사용하는 등 유효한 사례가 있다. 

 

하지만 강력한 방법은 아니므로 Controlled Input에 대해서 살펴보자. 

The Controlled

Controlled Input은 현재 값과 그 값을 변경하는 콜백을 props로 받아들인다. 

이는 보다 "React 적인" 접근 방법이라고 할 수 있다. ( 그렇다고 항상 이 방법을 사용해야 한다는 것은 아니다 )

<input value={someValue} onChange={handleChange} />

 

Input의 Value는 state로 어딘가에 존재하고 있다. 

일반적으로 입력을 렌더링 하는 컴포넌트 ( aka Form 컴포넌트 )는입력을 state로 저장한다 :

class Form extends Component {
  constructor() {
    super();
    this.state = {
      name: "",
    };
  }

  handleNameChange = (event) => {
    this.setState({ name: event.target.value });
  };

  render() {
    return (
      <div>
        <input type="text" value={this.state.name} onChange={this.handleNameChange} />
      </div>
    );
  }
}

 

( 물론 다른 컴포넌트의 상태가 될 수 있고, Redux와 같은 별도의 상태 저장소에 있을 수 있다. )

 

매번 우리는 새로운 문장을 입력하면, handleNameChange를 호출한다. 그러면 입력의 새 값을 받아 상태에 설정한다. 

[출처] 원글 - 입력 값에 따라 상태가 변경되는 모습 a를 입력 시 상태가 a로 변경, b를 입력했을 때 ab로 변경된다.

  • 상태는 빈 문자열로 시작합니다. - ''

  • 우리가 a를 입력하면 handleNameChange는 a를 받습니다. 그리고 setState를 호출합니다. 
    Input은 리렌더가 되고 value로 a를 가집니다. 

  • 다시 b를 입력하면 handleNameChange는 ab를 받고 state를 설정합니다. 
    Input은 한번 더 리렌더가 되어서 ab의 값을 가진다.  

위와 같은 플로우는 값의 변경 사항을 Form 컴포넌트에 "푸시 ( Push )" 합니다. 그러면 Form 컴포넌트는 

명시적으로 요청하지 않아도 항상 입력의 현재 값을 가지게 됩니다.

 

이 말은 우리의 Data (state)와 UI (inputs)이 항상 동기화되어 있다는 뜻이다. state는 Input에게 value를 주면

Input은 Form에게 현재 값을 변경하라고 요청한다. 

 

결국 Form 컴포넌트가 입력 변경에 즉시 응답할 수 있음을 의미한다. 예를들어 다음과 같다 :

  • 유효성 검사와 같은 in-place feedback

  • 모든 필드에 유효한 데이터가 없으면 버튼을 비활성화 한다. 

  • 신용카드 번호와 같은 특정 입력 형식 강제 적용 

하지만 이 모든 것들이 필요하지 않고 통제되지 않는 것이 더 간편하다고 생각한다면 그렇게 하면 된다. 

What makes an element "controlled"

물론 다른 요소들도 있습니다. Checkbox와 Select 그리고 Textarea 영역을 사용할 수 있습니다. 

 

Form 요소는 우리가 props를 통해서 값을 설정하는 경우 "Controlled" 요소가 됩니다. 

 

하지만 Form 요소마다 값을 설정하는데 사용되는 props가 다르므로 표를 통해 정리해보았다 :

Element Value property Change Callback New value in the callback
<input type="text" /> value="string" onChange event.target.value
<input type="checkbox" /> checked={boolean} onChange event.target.checked
<input type="radio" /> checked={boolean} onChange event.target.checked
<textarea /> value="string" onChange event.target.value
<select /> value="option value" onChange event.target.value

Conclusion

Controlled와 UnControlled 모두 장점이 있습니다. 구체적인 상황에서 각자에게 더 적합한 접근 방식을 선택하는 것이

중요하다. 

 

UI 측면에서 입력창이 단순하다면 ref를 사용해서 제어하지 않아도 괜찮다. 

다른 문서에서 "나쁘다"고 말하는 것을 들을 필요가 전혀 없다. 

 

feature uncontrolled controlled
일회성 값 검색 ( 예를들어 submit )
제출 시 유효성 검사
즉각적인 필드 유효성 검사
조건부 제출 버튼 비활성화
입력 형식 적용
하나의 데이터에 대한 여러 일력
동적 입력

 

또한 한번 결정되면 끝이 아니고 언제든지 Controlled Input으로 마이그레이션할 수 있습니다. 

Uncontrolled Input에서 Controlled Input으로 전환은 그렇게 어려운 작업이 아니다. 

반응형

'React > 실험실' 카테고리의 다른 글

Table 컴포넌트  (0) 2024.07.31
useFunnel 만들기  (2) 2024.06.16
[React] TDD, 클린 코드 with React 3기 - 1주차  (0) 2024.03.09
[React] Funnel 컴포넌트  (1) 2024.03.07
[React] PWA 그것은 무엇인가?  (0) 2024.03.03