[dev] uncontrolled input vs controlled input

Noah Ko·2022년 9월 19일
0

DevNote

목록 보기
31/31
post-thumbnail

차례

  1. input에 값을 적을 때 마다 나타나는 Error
  2. 왜 발생하는가?
  3. controlled input과 uncontrolled input의 차이점은?
  4. 어떤 걸 써야하는가?
  5. 해결책

1. input에 값을 적을 때마다 나타나는 Error

react에서 만든 application에서 input에 typing하자마자, 아래와 같은 에러가 콘솔에 찍혔다.

2. 왜 발생하는가?

에러메세지를 찬찬히 살펴보면,

Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.

이를 한글로 간단하게 번역하게되면 아래와 같은데,

컴포넌트가 제어되지 않는 인풋을 제어되도록 변경하고 있고, 이건 대게 값이 정의되지 않은 것에서 정의되는 값으로 변화 할 때 발생하는 일이다. 이건 일어나서는 안되는 일이다. 컴포넌트의 생애주기 동안 통제되는 인풋요소를 사용할지 통제되지 않는 인풋요소를 사용할지 결정해라

에러메세지를 보면 문제가 되는 점은 첫번째 문장임을 알 수 있다.

A component is changing an uncontrolled input to be controlled

즉, 컴포넌트가 통제되지 않는 인풋을 자꾸 통제되는 인풋으로 바꾸려고 하는게 문제란다. 이게 대체 무슨 말이란 말인가.

3. controlled input과 uncontrolled input의 차이점은?

뭐가 문제인지 알아봤으니, controlled input과 uncontrolled input이 뭔지 알아봐야 겠다. 결론부터 말하자면 둘의 차이는 input의 value가 react에 의해 관리 될 수 있는가 없는가에 있다. React 공식문서에 기재된 내용은 아래와 같다.

3-1. controlled input이란?

첫 번째 문장을 살펴보면, contorlled input의 명확한 정의를 알 수 있다.

An input form element whose value is controlled by React is called a controlled component.

즉, react에 의해서 value가 통제되는 것을 말한다. 이게 무슨말인지 이해하기 위해서는 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>
    );
  }
}

클래스형 컴포넌트는 클래스이기 때문에, constructor(생성자)를 선언해줘야 한다. 이 과정에서 react component가 해당 component 내부에서 사용되는 input의 value를 관리할수 있다.생성자에서 initial state를 선언하고 내부에서 사용된 input의 값을 update하는 함수도 component내부에서 관장한다.

이렇게 작성하게 되면, react에서 controll 할수 있는 component가 되는 것이다. 그렇다면 이렇게 질문할수도 있다.

그러면 클래스형 컴포넌트 생성자에서 initial state를 선언하지 않으면 되지 않는가?

맞다. 그렇게 작성한 것이 react에서 제어할 수 없는 uncontrolled input이 되는 것이다.

3-2. uncontrolled input이란?

다시한번 돌아가 uncontrolled input의 정의를 살펴보자면, 아래와 같다.

An uncontrolled component  works like form elements do outside of React.

이또한 글만 봐서는 무슨 말인지 감이 잘 오지 않는다. 그래서 조금 더 자세한 설명을 살펴보면 아래와 같은 글을 찾을 수 있었다.

The alternative is uncontrolled components, where form data is handled by the DOM itself.
즉, input의 value가 react에 의해 관리되는 것이 아니라, DOM에서 관리되고 있음을 의미한다.

여기서는 DOM이라고 하였지만, react가 아닌 모든 영역은 DOM이라고 생각하면 되지 않을까 싶다.
아래는 클래스형 컴포넌트에서 생성자에 initial state를 선언하지 않은 경우이다.

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

위를 살펴보면 controlled input과 다르게 input의 onChange가 아니라 Ref로 해당 Node에 focus하여 current.value가 변화할때 마다 그 값을 가져오는 방식으로 value를 update를 하고 있음을 볼 수 있다.

즉, 위와 같은 상태에서 컴포넌트가 초기 렌더링 되게 되면, 해당 input의 value는 undefined가 되고 이는 컴포넌트가 uncontrolled input이 되도록 한다. 다시말해 react가 initial state와 value를 확인하고 제어할 수 없기 때문에 uncontrolled input이라고 정의되는 것이다.

4. 어떤걸 써야 하는가?

React는

"네가 만약 코드를 빠르고 더럽게 짜기를 바라고 조금이나마 코드를 덜 쓰기 바란다면 uncontrolled input을 권할게, 그게 아니라면 controlled input을 쓰는게 좋을 거야" 라고 말하고 있다.

It can also be slightly less code if you want to be quick and dirty. Otherwise, you should usually use controlled components.
-react 공식문서 중에서-

controlled input을 사용하는 것이 좋을 것 같다.

5. 해결책

해결책은 의외로 간단하다. 결론적으로 말하면 맨처음 에러의 내용은 controlled와 uncontrolled 둘중에 하나를 선택하라는 내용이었고 공식문서에는 controlled input을 추천했으니, 지금 input의 initial state를 react가 알 수 있게 하면 된다.

그렇게 하기 위한 해결책에는 2가지가 있는데,

  1. defaultValue를 주는 것.
<input 
	defaultValue = ''
	onChange={.....}
/>
  1. undefined 일 때를 처리 하는 것
<input 
	value = {state || ''}
	onChange = {.....}
/>

이 두가지가 그 방법이다.

현재 에러가 뜨는 이유는 처음 렌더링 했을 때 value={undefined}이기 때문에 이에 대한 처리만 해준다면, react는 해당 input의 initial state를 알게 되었고, 그렇게 함으로써 이제 해당 input은 더이상 uncontrolled input이 아니게 되는 것이다.

6. 정리

DOM API를 사용해서 input의 value를 통제한다면, Uncontrolled input
React Props를 통해서 input의 value를 통제한다면, Controlled input

-끝-

출처
리액트 공식 문서(Controlled input vs Uncontrolled input)
리액트 공식 문서 (Controlled input)
리액트 공식 문서 (Uncontrolled input)
Stack overflow 해결법 예시

profile
아코 자네 개발이 하고 싶나

0개의 댓글