React에서 DOM 엘리먼트에 접근하기
- 자바스크릅트에서 제공하는 API를 이용해 DOM 조작을 하는 것이 리액트 방식을 이용하는 것보다 더 쉬운 경우가 있다.
- 이를 위해 리액트는 HTML 엘리먼트에 대한 DOM API에 접근할 수 있게하는 ref라는 도구를 제공한다.
- 또한 페이지 안의 어떤 HTML 엘리먼트든 랜더링 할 수 있는 포털(portal)이라는 기능도 제공한다.
ref 사용하기
- JSX는 단순히 DOM이 어떻게 보여야 할지를 기술한다. 그러나 실제 HTML을 대변하는 것은 아니다. 때문에 이를 보완하기 위해 리액트는 최종 렌더링된 HTML 엘리먼트와 JSX 사이를 연결해주는 ref(reference의 약자)라는 기능을 제공한다.
- ref 속성 추가
const self = this;
return (
<Element ref={
function(el) {
self._input = el;
}
}
/>
);
return (
<Element ref={
(el) => {
this._input = el;
}
}
/>
);
- 해당 컴포넌트가 마운트 되면 컴포넌트 내부 어디서든 self._input 혹은 this._input를 사용해 input 엘리먼트를 나타내는 HTML에 접근할 수 있다.
- JSX 내부에 작성한 익명함수는 컴포넌트가 마운트될 때 호출되며, 최종 HTML DOM 엘리먼트에 대한 참조를 매개변수로 받는다. 위 예제에서는 el이라고 했지만 매개변수 이름은 개발자가 변경 가능하다.
- 익명함수는 단순히 컴포넌트에 _input이라는 커스텀 속성을 만들고 그 속성에 DOM 엘리먼트의 값을 지정한다.
- ref 예제
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ''
}
this.showWelcomeMessage = this.showWelcomeMessage.bind(this);
}
showWelcomeMessage() {
alert(`환영합니다. ${this.state.name}!!`);
this.name_input.value = '';
this.name_input.focus();
}
render() {
return(
<div>
<input
type="text"
placeholder="이름을 입력해주세요"
ref={
(el) => {
this.name_input = el;
}
}
/>
<button
type="button"
onClick={this.showWelcomeMessage}
>
클릭
</button>
</div>
);
}
}
ReactDOM.render(
<Welcome/>,
document.getElementById('app')
)
16.3에서 ref 사용하기
ref에 접근하기
const node = this.myRef.current;
render
메서드 안에서 ref가 엘리먼트에게 전달되었을 때, 그 노드를 향한 참조는 ref의 current 프로퍼티에 담기게된다.
- ref의 값은 노드의 유형에 따라 다르다.
- ref 어트리뷰트가 HTML 엘리먼트에 쓰였다면, 생성자에서
React.createRef()
로 생성된 ref는 current 프로퍼티의 값으로 자신을 전달받은 DOM 엘리먼트를 받는다.
- ref 어트리뷰트가 커스텀 클래스 컴포넌트에 쓰였다면, ref 객체는 마운트된 컴포넌트의 인스턴스를 current 프로퍼티의 값으로 받습니다.
- 함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 프로퍼티를 사용할 수 없다.
포털 사용하기
- 리액트 컴포넌트의 랜더링 메서드가 반환하는 엘리먼트는 부모 노드에서 가장 가까운 자식 요소로 DOM에 마운트 된다. 즉 자식 요소는(컴포넌트, 엘리먼트 등) 부모 컴포넌트의 DOM 내부에 렌더링 된다
- 포탈을 사용하면 자식 요소를 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 렌더링 할 수 있다. 즉 자식 요소를 개발자가 원하는 위치에 랜더링 할 수 있다.
- 포탈을 사용하면 개발자가 작성한 JSX 계층구조에 종속되지 않으면서 컴포넌트를 렌더링 할 수 있다.
포탈 사용법
ReactDOM.createPortal(child, container)
- child : 엘리먼트, 문자열, 혹은 fragment와 같은 어떤 종류이든 렌더링할 수 있는 React 자식
- container : DOM 엘리먼트
- 위 두 매개변수를 전달받아 child를 container에 렌더링하는 메서드
- 예1
render() {
return (
<div>
{this.props.children}
</div>
);
}
render() {
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
- 예2
class Label extends React.Component {
render() {
return ReactDOM.createPortal(
` : Portal Test`,
document.getElementById('heading')
);
}
}
class Welcome extends React.Component {
constructor(props) {
super(props);
this.showWelcomeMessage = this.showWelcomeMessage.bind(this);
}
showWelcomeMessage() {
alert(`환영합니다. ${this.name_input.value}!!`);
this.name_input.value = '';
this.name_input.focus();
}
render() {
return(
<div>
<input
type="text"
placeholder="이름을 입력해주세요"
ref={
(el) => {
this.name_input = el;
}
}
/>
<button
type="button"
onClick={this.showWelcomeMessage}
>
클릭
</button>
<Label/>
</div>
);
}
}
ReactDOM.render(
<Welcome/>,
document.getElementById('app')
)