React의 컴포넌트를 설명할 때 Uncontrolled Component와 Controlled Component라는 개념이 있다. 이 둘에 대해서 살펴보자.
1. 왜 uncontrolled와 controlled 컴포넌트에 대해 알아야 할까?
- React는 내부의 상태(state)를 '신뢰 가능한 단일 소스(Single Source of Truth)'로 관리하려는 설계 원칙을 가지고 있다.
- 즉 자식 컴포넌트가 data가 필요할 경우, 해당 data는 가장 가까운 공통 부모 컴포넌트에게서만 props의 형태로 전달받아서 사용해야 한다.
- 대부분의 HTML 엘리먼트들(ex.
<div>
등)은 엘리먼트가 내부적으로 어떤 데이터를 가지지 않기 때문에 문제될 것이 없다.
하지만 HTML 엘리멘트 중 자체적으로 특정 data를 가지는 엘리먼트들이 있다. 바로 <form>
태그의 엘리먼트들이다.(<input>
, <textarea>
, <select>
등)
- 이들은 user가 DOM에서 어떤 정보를 입력하거나 선택할 경우, 해당 정보를 HTML 엘리먼트가 직접 보관하게 되는데, 이는 위에서 언급한 리액트의 핵심 설계원리인 '신뢰 가능한 단일 소스' 원칙에 위배되는 상황이다.
- 따라서 이를 해결하기 위해서 React에서 Controlled 컴포넌트의 개념이 나온 것이다.
하지만 무조건 Controlled Component로만 만드는 것이 항상 정답이라는 의미는 아니다. 상황에 따라서는 uncontrolled component로도 충분한 경우도 있다.
2. Uncontrolled Component : 'PULL' the value
Uncontrolled Component는 전통적인 HTML form input과 유사하다.
class Form extends React.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>
);
}
- 위 코드는 DOM에 의해서 자체적으로 유저가 상호작용한 정보가 담겨있다.
- 따라서 React Component가 DOM이 관리하는 정보를 알기 위해서는 실제 DOM에 접근할 수 있는 방법이 필요한데, 그것이 바로
<input>
태그 내부에 부여된ref
이다. ref
prop에 넘겨진 콜백함수는 componentDidMount() 또는 componentDidUpdate() 직전에 호출된다.- 따라서 componentDidMount()가 실행되는 시점에는 DOM에서 ref를 통해서 받아온 정보에 대한 참조를 저장할 수 있다.
- 그리고 최종적으로
handleSubmitClick
메소드가 버튼 클릭으로 실행될 때 해당 참조에 대한 정보를 컴포넌트 내부에 저장한 후 추가적인 작업을 할 수 있다. - 실질적으로는 React Component가 HTML 엘리먼트에서 정보를 PULL하여 사용하는 방식이다.
- 컴포넌트 내부에서 실시간으로 user 정보를 관리하는 것이 아니기 때문에 실시간으로 작업을 처리할 때(ex. 입력이 완료되기 전까지 버튼을 숨기는 등의 행위)에는 부적합한 방식이다.
3. Controlled Component : 'PUSH' the value
- Form element의 value를 component의 prop으로 설정하고 이를 활용하면 "Controlled Component"이다!
- 반면 Controlled Component는 현재 HTML 엘리먼트에 들어온 정보를 prop으로 state를 변경시키고, 변경된 state를 기반으로 HTML 엘리먼트의 value를 변경시키는 방식이다.
- DOM의 정보를 컴포넌트 내부에 state로 저장하고 state를 기반으로 HTML 엘리먼트를 다시 re-rendering시키기 때문에 이 방식이 더 React스러운 방식이라고 할 수 있다.
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>
);
}
}
위 코드가 돌아가는 방식을 살펴보자면..
- user가 정보를 input 태그에 입력하면 onChange 메소드가 실행되면서 Component 컴포넌트의 내부 state의 값을 유저가 입력한 값으로 변경시킨다.
- state가 update되면서 컴포넌트가 re-rendering되는데, re-rendering 시에 input 태그의 value에 현재 변경된 state를 부여한다.
- user 입력을 state로 저장 => re-rendering => user 입력을 value로 주어 실시간으로 화면에 반영
- 이 방식을 통해 React Component에 내장된 state를 기반으로 작동하도록 HTML form 태그를 변경시켜, this.state.name이라는 '신뢰 가능한 단일 소스'를 기반으로 컴포넌트가 작동되도록 변경하였다.
- user 정보를 기반으로 state를 변경시킨 후, 다시 해당 state를 input 태그의 value로 설정하고 있기 때문에 uncontrolled component와 달리 데이터를 'PUSH'하는 방식이라 할 수 있다.
이 방식을 취하면 react가 내부적으로 관리하는 state와 유저가 입력하는 정보 간의 sync가 맞기 때문에 uncontrolled component에서는 불가능했던 실시간 작업 처리가 가능해진다. 실시간으로 field validation을 체크하거나, 조건에 따라 submit button을 disabling하는 등 실시간으로 user에게 정보를 일러줘야 할 때에 사용하기 좋다.
'Javascript 공부 > TIL' 카테고리의 다른 글
200206(목) : Static Method vs. Instance Method (0) | 2020.02.06 |
---|---|
200131(금) : CORS (0) | 2020.01.31 |
200130(목) : Fetch API (0) | 2020.01.30 |
200129(수) : Deep copy vs. Shallow Copy (0) | 2020.01.29 |
200125(토) : Merge Sort, Quick Sort (0) | 2020.01.25 |
댓글