ref

Haechan Kim·2022년 1월 8일
0

React

목록 보기
5/13

일반 html에서 DOM 요소에 이름 달 때 id 사용

<div id = "my_element"></div>

요소에 id를 달면 css, js 에서 해당 id 갖고있는 요소 찾아서 작업 가능

  • ref : html에서 id 사용해 DOM에 이름 다는것처럼 리액트 프로젝트 내부에서 DOM에 이름 다는 방법. reference의 줄임말

리액트 컴포넌트 안에서도 id 사용할수는 있지만 권장 X
html에서 id는 유일해야 하는데 컴포넌트 여러번 사용한다고 할 때 중복 id 가진 DOM이 여러개 생기기 때문에 잘못된 사용

ref는 전역적 X, 컴포넌트 내부에서만 작동하기 때문에 이런 문제 X

  • ref는 어떤상황에서 사용?
    DOM을 꼭 직접적으로 건드려야 할 때 사용
    순수 js로 만든 웹사이트에서 input 검증할 때 특정 id가진 input에 클래스 설정해줌

but 리액트에서 이런 작업은 굳이 DOM에 접근하지 않아도 state로 구현 할 수 있다

  • DOM을 꼭 사용해야 하는 상황
    state만으로 해결할 수 없는 기능이 있다
    • 특정 input에 포커스 주기
    • 스크롤 박스 조작하기
    • Canvas 요소에 그림 그리기 등
      이런 경우에는 DOM에 직접 접근할 수 밖에 없는데 이때 ref를 사용한다.

ref를 사용하는 방법에는 두 가지가 있다.

  1. 콜백 함수를 통한 ref 설정
    가장 기본적인 방법
    ref를 달고자 하는 요소(여기서는 input)에 ref라는 콜백 함수를 props로 전달
    이 함수는 ref값을 파라미터로 받음
    파라미터(ref)를 컴포넌트의 멤버 변수로 설정
// 앞으로 this.input은 input 요소의 DOM을 가리킴
<input ref = {(ref) => {this.input = ref}} />

ref의 이름은 자유롭게 지정 가능

// this.superman은 input 가리킴
<input ref = {(ref) => {this.superman = ref}} />
  1. createRef
    리액트 내장 함수
    컴포넌트 내부에서 멤버 변수로 Reaxt.createRef()를 담는다
    그리고 해당 변수를 ref를 달고자 하는 요소에 ref props로 넣어준다

ref 이용해 ValidateSample에서 버튼 클릭 후에 자동으로 input창에 포커스 오도록

//ValidationSample.js
class ValidationSample extends Component {
    state = {
        password: '', 
        clicked: false,
        validated: false
    }

    handleChange = (e) => {
        this.setState({
            password: e.target.value
        });
    }

    handleButtonClick = () => {
        this.setState({
            clicked: true, 
            validated: this.state.password === '0000'
        })

        this.input.focus(); // 버튼에 onClick 이벤트 발생하고 input에 다시 커서 포커스 주도록
        // 이제 this.input이 컴포넌트 내부의 input요소 가리키고 있으니 일반 DOM 다루듯 코드 작성
      // 밑에서 this.superman이라고 했다면 this.superman.focus()

    }

    render() {
        return (
            <div>
                <input 
                ref = {(ref) => this.input = ref} // this.input이 컴포넌트 내부의 input요소를 가리키고 있음
               // this.superman = ref라고 한다면
                type = 'text'
                value = {this.state.password}
                onChange = {this.handleChange}
                className = {this.state.clicked ? (this.state.validated ? 'success' : 'failure') : ''}
                />
                
                <button onClick = {this.handleButtonClick}>검증하기</button> 
            </div>
        )
    }
}
  • 컴포넌트에 ref 달기
    주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때
<MyComponent ref = {(ref) => {this.MyComponent = ref}} />
// 이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에도 접근 가능
  • 스크롤박스 예시
// ScrollBox.js
class ScrollBox extends Component {
    // 스크롤바를 맨 밑으로 내리는 메서드
    // 이 메서드는 부모 컴포넌트인 App에서 SrollBox에 ref 달면 사용 가능
    scrollToBottom = () => {
        // scrollTop: 세로 스크롤바 위치
        // scrollHeight: 스크롤이 있는 박스 안의 div 높이 (가려진 부분까지 전체. 650)
        // clientHeight: 스크롤이 있는 박스의 높이 (보여지는 부분. 300)
        // 스크롤을 맨 아래까지 내리려면 scrollHeight에서 clientHeight 빼면 됨
        const {scrollHeight, clientHeight} = this.box;
        // 비구조화 할당 문법
        // const scrollHeight = this.box.ScrollHeight;
        // const clientHeight = this.box.clientHeight; 와 같은 의미
        this.box.scrollTop = scrollHeight - clientHeight;
    }

    render() {
        const style = {
            border: '1px solid red',
            height: '300px',
            width: '300px',
            overflow: 'auto',
            position: 'relative'
        };

        const innerStyle = {
            width: '100%',
            height: '650px',
            background: 'linear-gradient(yellow, blue)'
        }

        return (
            <div
                style = {style}
                // 최상위 DOM에 ref 달기
                ref = {(ref) => {this.box = ref}}>
                <div style = {innerStyle} />
            </div>
        );
    }
}

부모 컴포넌트인 App에서 ScrollBox에 ref 달고 (이름 scrollBox로)
버튼 누르면 ScrollBox의 scrollToBottom 메서드 실행

class App extends Component {
  render() {
    return (
      // App 컴포넌트(부모)에서 ScrollBox에 ref 달고 (이름 scrollBox로)
      // 버튼 누르면 ScrollBox의 scrollToBottom 메서드 실행
      <div>
        <ScrollBox ref = {(ref) => this.scrollBox = ref} />
        <button onClick = {() => this.scrollBox.scrollToBottom()} >
          맨 밑으로
        </button>
      </div>
    )
  }
}
  • 정리
    컴포넌트 내부에서 DOM에 직접 접근해야 할 때 ref 사용
    먼저 ref 안쓰고도 구현할 수 있는지 고려하고 사용
    서로 다른 컴포넌트끼리 데이터 교류시에는 ref 사용하면 안됨
    ㄴ 어긋난 설계
    반드시 부모 - 자식 흐름으로 교류해야 함

0개의 댓글