1220 - 1226
1220
Ref와 DOM
- Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공한다.
- 리액트 앱은 기본적으로 선언형 프로그래밍 사용이 권장되지만 모든 것을 선언형으로 작성할 수는 없다.
Ref를 사용해야 할 때
- 포커스, 텍스트 선택영역, 혹은 미디어의 재생을 관리할 때
- 애니메이션을 직접적으로 실행시킬 때
- 서드 파티 DOM 라이브러리를 React와 같이 사용할 때
DOM 요소에 접근/조작하려면 DOM API를 사용할 수 있지만, React는 ref를 통해 DOM에 접근/조작하는 방법을 제공한다.
즉, React는 ref를 통해 렌더링 이후 DOM 요소를 통해 접근/조작할 필요가 있다.
남용하지 말 것
- ref는 애플리케이션에 어떤 일이 일어나게 할 때 사용될 수도 있다.
- 그럴 때는 어느 컴포넌트 계층에서 상태를 소유해야 하는지 생각해보자
-> 상태를 상위 계층으로 끌어올려 문제를 해결할 수도 있다.
Ref 생성
- Ref는
React.createRef()
을 통해 생성되고 ref 어트리뷰트를 통해 React 엘리먼트에 부착된다. - 보통, 컴포넌트의 인스턴스가 생성될 때 Ref를 프로퍼티로서 추가하고, 그럼으로서 컴포넌트의 인스턴스의 어느 곳에서도 Ref에 접근할 수 있게 한다.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
Ref 접근
render 메서드 안에서 ref가 엘리먼트에게 전달되었을 때, 그 노드를 향한 참조는 ref의 current 어트리뷰트에 담기게 된다.
- ref 어트리뷰트가 HTML 엘리먼트에 쓰였다면, 생성자에서
React.createRef()
로 생성된 ref는 자신을 전달받은 돔 엘리먼트를 current 프로퍼티의 값으로서 받는다. - ref 어트리뷰트가 커스텀 클래스 컴포넌트에 쓰였다면, ref 객체는 마운트된 컴포넌트의 인스턴스를 current 프로퍼티의 값으로서 받는다.
- 함수 컴포넌트는 인스턴스가 없기 때문에 ref 어트리뷰트를 사용할 수 없다.
DOM 엘리먼트에 Ref 사용하기
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// textInput DOM 엘리먼트를 저장하기 위한 ref를 생성합니다.
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// DOM API를 사용하여 명시적으로 text 타입의 input 엘리먼트를 포커스합니다.
// 주의: 우리는 지금 DOM 노드를 얻기 위해 "current" 프로퍼티에 접근하고 있습니다.
this.textInput.current.focus();
}
render() {
// React에게 우리가 text 타입의 input 엘리먼트를
// 우리가 생성자에서 생성한 `textInput` ref와 연결하고 싶다고 이야기합니다.
return (
<div>
<input type="text" ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
- focusTextInput에 bind 메서드를 호출한 이유는 focusTextInput 메서드 this 바인딩을 위해서이다.
- 이 방법을 피하기 위해서는 메서드가 아닌 화살표 함수로 작성하면 된다.
- DOM 엘리먼트에 ref 어트리뷰트를 사용하면 ref의 current 프로퍼티에 해당 요소의 참조가 저장된다.
클래스 컴포넌트에 ref 사용하기
방금 작성한 CustomTextInput 컴포넌트의 인스턴스가 마운트 된 이후에 즉시 클릭되는 걸 흉내내기 위해 CustomTextInput 컴포넌트를 감싸는 걸 원한다면, ref를 사용하여 컴포넌트의 인스턴스에 접근하고 직접 자식 컴포넌트의 메서드를 호출할 수 있다.
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
}
render() {
return <CustomTextInput ref={this.textInput} />;
}
}
Ref와 함수 컴포넌트
- 함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 어트리뷰트를 사용할 수 없다.
- 함수 컴포넌트에 ref를 사용할 수 있도록 하려면, forwardRef를 사용하거나 클래스 컴포넌트로 변경할 수 있다.
- 다만, DOM 엘리먼트나 클래스 컴포넌트의 인스턴스에 접근하기 위해 ref 어트리뷰트를 함수 컴포넌트에서 사용하는 것은 된다.
부모 컴포넌트에게 DOM ref를 공개하기
- 부모 컴포넌트에서 자식 컴포넌트의 돔 노드에 접근하려 하는 경우도 있다.
- 자식 컴포넌트의 돔 노드에 접근하는 것은 컴포넌트의 캡슐화를 파괴하기 때문에 권장되지 않는다.
- 자식 컴포넌트에 ref를 사용할 수 있지만, 이 방법은 자식 컴포넌트의 인스턴스의 돔 노드가 아닌 자식 컴포넌트의 인스턴스를 가져온다는 점에서, 자식 컴포넌트가 함수 컴포넌트인 경우에는 동작하지 않는다는 점에서 좋은 방법이 아니다.
- ref 전달하기 (ref forwarding)을 사용할 것
- ref 전달하기는 컴포넌트가 자식 컴포넌트의 ref를 자신의 ref로서 외부에 노출시키게 한다.
콜백 Ref
- ref가 설정되고 해제되는 상황을 세세하게 다룰 수 있는 방법.
- ref 어트리뷰트에 React.createRef를 통해 생성된 ref를 전달하는 대신, 함수를 전달한다.
- 전달된 함수는 다른 곳에 저장되고 접근될 수 있는 리액트 컴포넌트의 인스턴스나 돔 엘리먼트의 인자로서 받는다.
export default class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = (element) => {
this.textInput = element;
};
this.focusTextInput = () => {
// DOM API를 사용하여 text 타입의 input 엘리먼트를 포커스합니다.
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// 마운트 되었을 때 자동으로 text 타입의 input 엘리먼트를 포커스합니다.
this.focusTextInput();
}
render() {
// text 타입의 input 엘리먼트의 참조를 인스턴스의 프로퍼티
// (예를 들어`this.textInput`)에 저장하기 위해 `ref` 콜백을 사용합니다.
return (
<div>
<input type="text" ref={this.setTextInputRef} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
- 컴포넌트의 인스턴스가 마운트 될 때 React는 ref 콜백을 DOM 엘리먼트와 함께 호출한다.
- 컴포넌트의 인스턴스의 마운트가 해제될 때, ref 콜백을 null과 함께 호출한다.
- ref 콜백들은 componentDidMount 또는 componentDidUpdate가 호출되기 전에 호출된다.
- 콜백 ref 또한 React.create를 통해 생성했었던 객체 ref와 같이 다른 컴포넌트에게 전달할 수 있다.
reference
리액트 공식 문서 - ref와 DOM