본문 바로가기

# Tech/React

[Typescript React] 컴포넌트 제어


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