본문 바로가기

프론트엔드(Front-End)/React

[REACT] 리액트의 ref

리액트의 ref란?

  • 리액트의 ref는 리액트 내부에서 state등으로 핸들링 할 수 없는 경우, 혹은 직접적으로 DOM에 접근하여 작업을 처리하거나 핸들링하기 위하여 사용합니다.
  • 간단히 설명하자면 ref는 리액트 컴포넌트의 id입니다. html 태그의 id와 같은 역할을 합니다.
  • ref는 다루고자 하는 컴포넌트에 ref 속성을 assign 함으로써 사용할 수 있습니다.
  • 리액트 ref가 유용하게 쓰이는 경우는 특정 input에 focus를 주는 경우, 스크롤 박스를 접근하는 경우, Canvas 요소에 그림을 그리는 경우에 유용하게 사용됩니다.
  • ref를 부여하면 DOM의 여러가지 속성 값에 접근할 수 있어 리액트의 일반적인 데이터의 흐름 (props, 부모 → 자식)을 거스를 수 있기 때문에 ref 통해 다른 데이터에 접근하는 것은 지양해야 합니다.
  • 만약 ref를 이러한 용도로 사용하게 된다면, 코드가 스파게티 코드가 될 수 있고 유지 보수 난이도도 올라갈 수 밖에 없게 됩니다.

ref 사용하는 방법

  • ref는 컴포넌트의 종류에 따라 사용법이 서로 조금씩 다릅니다.

함수형 컴포넌트

  • useRef 라는 Hook을 사용합니다.
  • React.creatRef() 메서드를 통해 ref를 담을 변수를 선언합니다.
import React, { useState, useRef } from 'react'

const FuncRefComp01 = () => {
    const [password, setPassword] = useState('')
    const [isClicked, setIsClicked] = useState(false)

    let inputEle = React.createRef()

    // handleChange
    const handleChange = (e) => {
        setPassword(e.target.value)
    }

    // handleButtonClick
    const handleButtonClick = () => {
        setIsClicked(true)
        showAlert(password)
    }

    const checkValidation = (password) => {
        if (password === '0000') {
            return true
        }
        return false
    }

    // handleKeyDown
    const handleKeyDown = (e) => {
        if (e.key === 'Enter') {
            setIsClicked(true)
            showAlert(password)
        }
    }

    const showAlert = (password) => {
        if (checkValidation(password)) {
            alert('비밀번호를 잘 입력하셨습니다.')
        } else {
            alert('비밀번호를 다시 확인 해주세요.')
        }
    }

    return (
        <>
            <div className="comp func-comp ref-comp">
                <input
                    type="password"
                    value={password}
                    onChange={handleChange}
                    onKeyDown={handleKeyDown}
                    className={
                        // 클래스명에 함수를 주면, 동적으로 검사를 해준다.
                        // 그러나 변수를 주게 되면 동적으로 검사하지 못해
                        // 별도로 변수를 체크해주는 함수와 함께 쓰는 등의 문법이 필요하다.
                        isClicked
                            ? checkValidation(password)
                                ? 'success'
                                : 'fail'
                            : ''
                        // checkValidation(password) ? 'success' : 'fail' // 이렇게 쓰면 동적 체크 가능
                    }
                    // ref={(ref) => {
                    //     inputEle = ref
                    //     //  함수형 컴포넌트에서 변수를 선언했을 경우 this로 가리키면 안된다.
                    // }}
                    // 아래처럼 축약해서 사용 가능함!
                    ref={inputEle}
                />
                <button
                    onClick={() => {
                        handleButtonClick()
                        inputEle.current.focus()
                        // ref 변수를 참조 할 때는 반드시 current 프로퍼티러 접근!
                    }}
                >
                    확인
                </button>
            </div>
        </>
    )
}

export default FuncRefComp01

실제 페이지

 


 

클래스형 컴포넌트

  • 클래스형 컴포넌트도 마찬가지로 React.createRef()를 통해 변수를 생성하고
  • 그 변수로 접근해서 current 프로퍼티를 통해 값을 처리하는 것은 같습니다.
import React, { Component, Fragment } from 'react'

class ClassRefComp02 extends Component {
    input = React.createRef()

    handleFocus = () => {
        this.input.current.focus()
    }

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

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

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

        return (
            <>
                <div className="comp class-comp ref-comp">
                    <input ref={this.input} />
                    <button
                        onClick={() => {
                            this.handleFocus()
                        }}
                    >
                        포커스
                    </button>
                    <button onClick={this.scrollToTop}>스크롤 UP</button>
                    <button onClick={this.scrollToBottom}>스크롤 DOWN</button>
                </div>
                {/* 스크롤 박스! */}
                <div className="comp class-comp ref-comp">
                    <div
                        style={style}
                        ref={(ref) => {
                            this.box = ref
                            // 별도로 변수를 선언하진 않았지만, 이런식으로 작성하면
                            // 자동으로 변수가 선언되 효과가 있으며 정상 작동함.
                        }}
                    >
                        <div style={innerStyle}></div>
                    </div>
                </div>
            </>
        )
    }
}

export default ClassRefComp02

실제 페이지