[React] 6. ref(Reference)

🏃Dekay (JuniorDeveloper)·2021년 9월 23일
0

React

목록 보기
6/13
post-thumbnail

Reference ✔

  • ref(Reference)HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법을 뜻한다.
  • 특정 DOM 요소에 작업을 해야할 때 id를 달면 css에서 특정 id특정 스타일을 적용하거나 자바스크립트에서 해당 id를 가진 요소를 찾아 작업할 수 있다.

그럼 컴포넌트 안에서 id를 사용해도 되나❓❗❗

리액트 컴포넌트안에서 id를 사용할 수 있다. JSX 안에서 DOMid를 달면 해당 DOM을 렌더링할 때 그대로 전달되지만 권장하지 않는다고 함.
👉 예를 들어, HTML에서 DOMid는 유일해야 하는데, 같은 컴포넌트를 여러 번 사용하게 되면 중복 id를 가진 DOM이 여러 개 생기기 때문에 권장하지 않는다.

1. ref를 사용해야 하는 상황

  • 특정 DOM에 작업을 해야할 때 ref를 사용한다는 것은 이해했지만, 조금더 쉽게 말하자면 DOM을 반드시 직접적으로 건드려야 할 때 사용해야 한다고 이해하자.
  • 실습을 해보기 위해 클래스형 컴포넌트에서 ref를 사용해보자.

1.1 예제 컴포넌트 생성

  • src/ValidationSample.csssrc/ValidationSample.js 파일 생성 후 아래와 같이 코드를 작성했다.
//ValidationSample.css
.success {
    background-color: lightgreen;
}

.failure {
    background-color: lightcoral;
}

//ValidationSample.js
import React, {Component} from "react";
import './ValidationSample.css'

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

    render() {
        return (
            <div>
                <input 
                type="password"
                value={this.state.password}
                onChange={this.handleChange}
                className={this.state.clicked ? (this.state.validated ? 'success' : 'failure') : ''}
                />
                <button onClick={this.handleButtonClick}>검증하기</button>
            </div>
        );
    }
}

export default ValidationSample;
  • 위의 코드는 input에서 onChange 이벤트가 발생하면 handleChange를 호출하여 statepassword 값을 업데이트 하는 코드이다.
  • button에서는 onClick 이벤트가 발생하면 handleButtonClick을 호출하여 clicked 값을 참으로 설정했고, validated 값을 검증 결과로 설정했다.
  • inputclassName 값은 버튼을 누르기 전에 비어있는 문자열을 전달하고, 버튼을 누른 후에는 검증 결과에 따라 success, failure 값을 설정하여 input 색상이 초록색 또는 빨간색 으로 나타난다.

1.2 DOM을 꼭 사용해야 하는 상황

  • 여태 살펴본 예제들은 state를 사용하여 필요한 기능을 구현했지만, 다음과 같이 state만으로 해결할 수 없는 기능이 있다.
  1. 특정 input포커스 주기
  2. 스크롤 박스 조작하기
    Canvas 요소에 그림 그리기 등등..
  • 위와 같은 상황은 DOM에 직접적으로 접근해야 하기 때문에 ref를 사용해야 한다.

2. ref 사용 ✏

  • 프로젝트에서 ref를 사용하는 방법은 두 가지로 나뉜다.

2.1 콜백 함수를 통한 ref 설정

  • ref를 만드는 가장 기본적인 방법이 콜백 함수를 사용하는 것이다.
  • ref를 달고자 하는 요소ref라는 콜백 함수를 props로 전달하여 사용한다.
    * 쉽게 말해서, 콜백 함수ref 값을 파라미터로 전달받고, 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정한다.
//콜백 함수 예시
<input ref={(ref) => {this.input=ref}} />
  • 위의 코드에서 this.inputinput 요소DOM을 가르킨다.
    또한, ref의 이름은 원하는 것으로 자유롭게 설정할 수 있다. (ex. this.dekay = ref)

2.2 createRef를 통한 ref 설정

  • createRef리액트에 내장되어 있는 함수이다.
  • 리액트 v16.3 부터 도입되었으며 이전 버전에서 동작 불가하니 주의❗❗
import React, { Component } from "react";

class ValidationSample extends Component {
  input = React.createRef();

  handleFocus = () => {
    this.input.current.focus();
  }
  
  render () {
    return (
      <div>
        <input ref={this.input} />
      </div>
    );
  }
}

export default ValidationSample;
  • createRef를 사용하여 ref를 만들기 위해서는 컴포넌트 내부에서 멤버 변수React.createRef()를 담아주어야 한다.
  • 그 후, 해당 멤버 변수ref를 달고자 하는 요소에 ref props로 넣어주면 ref 설정이 된다.
  • 설정하고 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 된다.
    * 콜백 함수를 사용할 때와 다른점이다.✔

2.3 ValidationSample에 적용해보기

  • ValidationSample.js를 렌더링 해보면 검증 하기를 눌렀을 때 input 요소의 텍스트 커서가 더 이상 보이지 않는다.
  • 버튼을 눌렀을 때, 포커스가 다시 input 쪽으로 자동으로 넘어가도록 코드를 작성해봤다.

2.3.1 input에 ref 달기

  • 콜백 함수를 사용하여 ValidationSample 컴포넌트에 ref를 달았다.
//ValidationSample.js
(...)
  <input
   ref={(ref) => this.input=ref}
   (...)
  />
  • 위의 코드를 참고하여 버튼 onClick 이벤트 코드를 수정했다.
import React, {Component} from "react";
import './ValidationSample.css'

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

    render() {
        return (
            <div>
                <input 
                ref={(ref) => this.input=ref}
                type="password"
                value={this.state.password}
                onChange={this.handleChange}
                className={this.state.clicked ? (this.state.validated ? 'success' : 'failure') : ''}
                />
                <button onClick={this.handleButtonClick}>검증하기</button>
            </div>
        );
    }
}

export default ValidationSample;
  • this.input이 컴포넌트 내부의 input 요소를 가르키고 있기 때문에 일반 DOM을 다루듯이 위의 코드 처럼 작성하면 된다.

3. 컴포넌트에 ref 달기

  • 리액트에서는 컴포넌트에 ref를 달아야 하는 경우가 있는데, 주로 컴포넌트 배우에 있는 DOM을 컴포넌트 외부에서 사용할 때 사용한다.
  • 컴포넌트에 ref를 다는 방법은 DOM에 ref를 다는 방법과 같다.

3.1 사용법

<MyComponent
  ref={(ref) => {this.myComponent=ref}}
/>
  • 위의 코드 처럼 정의하면 MyComponent 내부의 메서드멤버 변수에 접근할 수 있다.
  • 즉 내부의 ref에도 접근할 수 있다.
    (ex: myComponent, handleClick, input 등)

3.2 간단한 예제✨

  • 컴포넌트ref를 다는 것이 익숙하지 않아서 간단한 예제를 만들어 보려고 한다.
  • 스크롤 박스가 있는 컴포넌트를 하나 만들고, 스크롤바 아래로 내리는 작업을 부모 컴포넌트에서 실행해 보자.

3.2.1 컴포넌트 생성

  • scr/ScrollBox라는 컴포넌트를 만들어 JSX의 인라인 스타일링 문법으로 스크롤 박스를 생성했고, 최상위 DOMref를 달았다.
//ScrollBox.js
class ScrollBox extends Component {
    render() {
        const style = {
            border: '1px solid black',
            height: '300px',
            width: '300px',
            overflow: 'auto',
            position: 'relative'
        };

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

        return (
            <div
            style={style}
            ref={(ref) => {this.box=ref}}>
                <div style={innerStyle} />
            </div>
        )
    }
}

3.2.2 컴포넌트에 메서드 생성

  • 컴포넌트스크롤바를 맨 아래쪽으로 내리는 메서드를 생성해보자.
  • 자바스크립트스크롤바를 내릴 때는 DOM 노드가 가진 다음 값들을 사용했다.
    • scrollTop: 세로 스크롤바 위치(0~350)
    • scrollHeight: 스크롤이 있는 박스 안의 div 높이(650)
    • clientHeight: 스크롤이 있는 박스 높이(300)
  • 스크롤바를 맨 아래쪽으로 내리기 위해서 scrollHeight에서 clientHeight 높이를 빼주었다.
//ScrollBox.js
class ScrollBox extends Component {

    scrollToBottom = () => {
        const { scrollHeight, clientHeight } = this.box;
        this.box.scrollTop = scrollHeight - clientHeight;
    }

    render() {
        (...)
    }
}
  • scrollToBottom 메서드의 첫 번째 줄에서는 계속 언급한대로 ES6의 비구조화 할당 문법을 사용한 것이다.

3.2.3 컴포넌트에 ref 달고 내부 메서드 사용

  • App 컴포넌트에서 ScrollBoxref를 달고 버튼을 만들어서 누르면, ScrollBox 컴포넌트의 scrollToBottom 메서드를 실행하도록 코드를 작성했다.
//App.js
class App extends Component {
  render() {
    return (
      <div>
        <ScrollBox ref={(ref) => (this.scrollBox = ref)} />
        <button onClick={() => this.scrollBox.scrollToBottom()}>
          맨 밑으로
        </button>
      </div>
    );
  }
}

주의 ❗❗

  • 3.2.3 절 코드에서 문법상으로는 onClick={this.scrollBox.scrollToBottom}와 같은 형식으로 해도 틀린것은 아니다.
  • 하지만, 컴포넌트가 처음 렌더링될 때는 this.scrollBox 값이 undefined이므로 this.scrollBox.scrollToBottom 값을 인식하지 못하는 오류가 발생할 수 있다.
  • 이런 경우에는 화살표 함수 문법을 사용하여 아예 새로운 함수를 만들고 내부에서 this.scrollBox.scrollToBottom 메서드를 실행하는 것 처럼 사용하자.
    * 왜냐하면 버튼을 누를 때 이미 한 번 렌더링을 해서 this.scrollBox를 설정한 시점이라 오류가 발생안함.✔

end

profile
Believe you can & you're half way there 🙏

0개의 댓글