컴포넌트를 빌드한 후에는 render()
에서 반환된 컴포넌트 인스턴스에 접근하거나 메소드를 호출할 방법이 필요할 수도 있습니다. 반응적 데이터 플로우가 render()
의 결과물에서 최신의 props
가 각각의 자식으로 보내진 것을 항상 보장하기 때문에 애플리케이션의 데이터 플로우를 만드는데 대체로 이런 작업은 필요가 없지만, 여전히 이런 작업이 필요하거나 유리한 경우가 있긴 합니다. React는 이를 위해 refs
라는 탈출구를 제공합니다. refs
(레퍼런스)는 특히 다음과 같은 경우 유용합니다: 컴포넌트에 의해 렌더된 DOM 마크업을 찾을때 (인스턴스내의 절대적인 위치), 큰 프로젝트의 일부에 React 컴포넌트를 사용하는 경우 또는 기존의 코드베이스를 React로 변경하는 경우.
ref를 어떻게 얻는지 살펴보고, 예제를 완성해 봅시다.
컴포넌트에서 정의한, 가상의 DOM 엘리먼트를 반환하는 render()
와 헷갈리지 않도록 합니다. ReactDOM.render()는 컴포넌트의 지원 인스턴스(backing instance)에 대한 참조를 반환 합니다. 상태를 가지지 않는 컴포넌트에 대해선 null
을 반환하죠.
var myComponent = ReactDOM.render(<MyComponent />, myContainer);
명심하세요. 위의 코드는 컴포넌트 인스턴스를 반환하지 않습니다! 이는 그저 ReactElement:React에 마운트된 컴포넌트가 어떻게 보여야 할지 알려주는 경량의 표상(representation) 입니다.
var myComponentElement = <MyComponent />; // 이건 그냥 ReactElement 입니다.
// 여기 코드가 위치합니다...
var myComponentInstance = ReactDOM.render(myComponentElement, myContainer);
myComponentInstance.doSomething();
주의:
이는 오직 최상위 레벨에서만 사용되어야 합니다. 컴포넌트의 내부에서는
props
와state
가 자식 컴포넌트와 통신하도록 하거나 문자열이나 콜백 어트리뷰트 등 ref를 얻어오는 다른 방법을 사용하도록 하세요.
React는 어떤 컴포넌트에든 추가할 수 있는 특별한 어트리뷰트를 제공합니다. ref
어트리뷰트는 콜백 함수일 수 있으며 이는 컴포넌트가 마운트 된 후 즉시 실행됩니다. 참조된 컴포넌트는 파라미터로 전달될 것이며 콜백 함수는 즉시, 혹은 미래에 사용하기 위해 컴포넌트의 참조를 저장합니다.
render
에서 반환되는 무엇에든 ref
어트리뷰트를 추가하는 것은 간단합니다:
render: function() {
return (
<TextInput
ref={function(input) {
if (input != null) {
input.focus();
}
}} />
);
},
ES6 화살표 함수를 사용한다면 다음과 같습니다:
render: function() {
return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
this._input.focus();
},
<div />
같은 DOM 컴포넌트에 ref를 추가하면 DOM 노드를 얻게 됩니다; <TextInput />
같은 합성 컴포넌트에 ref를 추가하면 React 클래스 인스턴스를 얻게 됩니다. 후자의 경우, 클래스에 정의되어 있는 노출된 메소드를 호출할 수 있습니다.
참조된 컴포넌트가 언마운트되고 ref가 변경되면 이전의 ref는 null
을 인수로 호출됨을 주의 하세요. 이는 첫번째 예제에서처럼 인스턴트가 저장된 경우의 메모리 누수를 방지합니다. 또한 예제와 같이 ref를 인라인 함수 표현식으로 서술하게되면 React는 모든 업데이트에 대해 다른 함수 객체를 참조하게 됩니다. ref는 컴포넌트의 인스턴스의 호출 이전에 null
객체와 함께 즉시 호출될 것입니다.
React는 ref에 콜백 대신 문자열도 지원합니다 이런 접근은 이제 과거의 것이긴 합니다.
render
에서 반환된 값을 ref
어트리뷰트에 할당합니다:
<input ref="myInput" />
다른 코드(일반적으로는 이벤트 핸들러 코드)에서 this.refs
를 통해 지원 인스턴스에 접근 합니다:
var input = this.refs.myInput;
var inputValue = input.value;
var inputRect = input.getBoundingClientRect();
React 컴포넌트의 참조를 얻기 위해서, 현재의 React 컴포넌트를 위해서는 this
를, 소유한 컴포넌트에 대해서는 ref를 사용할 수 있습니다. 다음과 같이 작동 합니다:
var MyComponent = React.createClass({
handleClick: function() {
// 명시적으로 텍스트 인풋에 포커스하기 위해 raw DOM API를 사용합니다.
this.myTextInput.focus();
},
render: function() {
// ref 어트리뷰트는 컴포넌트가 마운트되면
// this.refs에 컴포넌트에 대한 참조를 추가합니다.
return (
<div>
<input type="text" ref={(ref) => this.myTextInput = ref} />
<input
type="button"
value="Focus the text input"
onClick={this.handleClick}
/>
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);
이 예제에서, 텍스트 인풋의 지원 인스턴스에 대한 참조를 얻었으며 버튼이 클릭되었을때 focus()
를 호출했습니다.
합성 컴포넌트에서는 참조는 컴포넌트 클래스의 인스턴스를 가리키기 때문에 클래스에 정의된 어떤 메소드도 호출할 수 있습니다. 컴포넌트의 기저에 있는 DOM 노드에 접근하기 위해서는 ReactDOM.findDOMNode를 "탈출구"로 사용할 수 있습니다. 하지만 이는 캡슐화를 깨며, 대부분의 경우 React 모델을 이용해 더 명확한 방법으로 구조를 짤 수 있기 때문에 추천하지 않습니다.
Refs는 반응적인 props
와 state
스트리밍을 통해서는 불편했던 특정한 자식 인스턴스에 메시지 보내기를 수행하는 좋은 방법입니다. 하지만 애플리케이션의 데이터 플로우 전반에 사용해도 되는 go-to 같은 개념은 아닙니다. 기본적으로는 반응적인 데이터 플로우를 사용하고, ref
는 근본적으로 반응적이지 않은 경우에만 사용하세요.
this.refs.myTypeahead.reset()
) 대부분, 이는 선언적으로 refs를 사용하는 것보다 내장 React 데이터 흐름을 명확하게 합니다.<input />
같은 "기본" 컴포넌트를 다루고 ref를 통해 그 기저의 DOM 노드에 접근해야 합니다. Refs는 이 일을 확실하게 수행하는 몇 안 되는 실용적인 방법의 하나입니다.state
가 컴포넌트 계층구조의 어느 부분에서 관리되어야 할지 비판적으로 생각해 봅시다. 대개는 state가 계층구조의 더 높은 레벨에서 "소유"하는 것이 타당함이 명확해집니다. 그렇게 함으로써 ref
를 사용해 "무언가 일어나도록" 하려는 욕망이 대부분 제거됩니다. - 대신에 데이터 플로우를 통해 대개 원하는 바를 달성하게 될 것입니다.