form 요소는 또 다른 내부 데이터를 갖는다.
리액트에서 제공하는 HTML 컴포넌트 중 form 요소는 특별하게 작동합니다.
폼 요소 내부에 담긴 데이터는 this.state와는 다르기 때문이죠.
아래의 코드를 한번 살펴볼까요?
interface Prop {} interface State { name: string; } class NameForm extends React.Component<Prop, State> { constructor(prop: Prop) { super(prop); this.setState({ name: "" }); } render() { return ( <form> <input id="name" type="text" value="type your name" /> </form> ); } }
위 코드에서 this.state.name와 #name.value는 따로 놉니다.
사용자가 폼의 값을 바꾸어도 컴포넌트의 상태는 변경시킬 수 없죠.
컴포넌트 안에 있는 폼의 내부데이터는
컴포넌트의 상태와 다르다는 것을 이해해야 합니다.
컴포넌트는 제어할 수 있다.
하지만 상태와 내부 데이터가 일치하는 편이 더 좋지 않을까요?
상태가 바뀌면 사용자가 보게될 폼의 값도 바뀌는게 더 직관적이죠.
리액트는 이 문제를 해결하기 위해 상태로부터 제어되는 컴포넌트controlled component from state라는 방법을 제안합니다.
폼의 데이터의 값이 컴포넌트의 상태와 일치되도록 제어하는 것이죠.
이 기법을 사용하기 위한 가이드라인은 다음과 같습니다.
- 폼의 데이터는 컴포넌트 상태에 종속되어야 한다.
- 폼의 데이터가 변경되면 컴포넌트에게 알린다.
가이드 라인을 천천히 따라가볼까요?
먼저 폼의 데이터가 컴포넌트 상태에 종속되어야 합니다.
폼의 데이터가 컴포넌트 상태를 참조하면 되겠죠. 아래처럼요.
render() { return ( <form> <input type="text" value={this.state.name} /> </form> ); }
하지만 여전히 문제는 남아있습니다.
사용자가 폼의 데이터를 수정하면 어떻게될까요?
폼의 내부 데이터와 컴포넌트 상태는 별개이기 때문에,
여전히 컴포넌트 상태를 변경시킬 수 없습니다.
폼의 내부 데이터가 변경되면 컴포넌트에게 알려줄 수 있는 방법이 필요한데.
폼 내부에 onChange 이벤트 핸들러를 달면 간단하게 해결할 수 있습니다.
onNameChange(event: React.FormEvent<HTMLInputElement>) { this.setState({ name: event.currentTarget.value }); } render() { return ( <form> <input type="text" value={this.state.name} onChange={this.onNameChange.bind(this)} /> </form> ); }
또한 이름을 대문자로 강제하고 싶다면,
이벤트 핸들러를 아래처럼 고치면 됩니다.
onNameChange(event: React.FormEvent<HTMLInputElement>) { this.setState({ name: event.currentTarget.value.toUpperCase() }); }
이제 상태와 내부 데이터는 완벽히 일치하겠죠?
컴포넌트의 상태를 바꾸면 폼의 내부 데이터가 바뀐다고 확신할 수 있습니다.
비슷한 데이터를 여러군데서 관리하는 대신에,
단 하나의 신뢰성 있는 데이터로 합쳐서 사용하는 것.
이것을 신뢰 가능한 단일소스single source of turth로 만든다고 합니다.
여기서는 컴포넌트의 상태가 신뢰 가능한 단일소스가 되겠네요.
컴포넌트는 제어하지 않을 수 있다.
반면에 제어를 하지않는 경우도 있습니다.
이것을 제어되지 않는 컴포넌트uncontrolled component라고 하죠.
클라이언트가 보내준 데이터를 전적으로 신뢰하는 것 처럼,
데이터를 컴포넌트 측에서 바꾸지 않을 때가 대표적입니다.
다만 상당히 흑마법같은 것으로 취급하고 있기 때문에,
리액트 개발자가 왠만하면 제어되는 컴포넌트로 사용할 것을 권고했습니다.
제어되지 않는 컴포넌트에서는 State로 내부 데이터를 얻을 수 없기 때문에,
ref라는 테크닉을 이용해서 HTMLElement를 얻은 뒤 사용해야 합니다.
interface Prop {} interface State { name: string; } class NameForm extends React.Component<Prop, State> { private _name: HTMLElement | null = null; constructor(prop: Prop) { super(prop); this.setState({ name: "" }); } getNameValue(): string { if (this._name) return this._name.innerText; return ""; } render() { return ( <form> <input type="text" ref={input => (this._name = input)} /> </form> ); } }
'# Tech > React' 카테고리의 다른 글
Next.JS에서 동적 세그먼트 사용하기 (0) | 2020.01.19 |
---|---|
[Typescript React] 상태 끌어올리기 (0) | 2019.12.07 |
[Typescript React] 리스트 컴포넌트 (0) | 2019.12.07 |
[Typescript React] 상태 관리 라이브러리 (0) | 2019.12.06 |
[Typescript React] 조건부 렌더링 (0) | 2019.12.06 |