useRef을 사용한 DOM 접근과 Ref.current를 cleanup 함수에서 사용할 시 주의할 점

서동경·2023년 10월 14일
0

troubleshooting

목록 보기
2/13
post-thumbnail

리액트에서의 DOM 접근

리액트에서 DOM에 직접 접근하기 위해서는 id나 class를 통해 접근하는 DOM API를 사용하지 않는다. 기존 DOM API를 이용하여 실제 DOM을 직접 조작하는 것은 상태를 기반으로 가상 돔을 제어하는 리액트 시스템을 벗어나고, 이렇게 리액트 라이프 사이클과 관련없이 가져온 DOM은 신뢰하기 어렵다.

useRef를 사용하면 리액트의 관리 하에 라이프 사이클을 고려하여 DOM을 조작할 수 있다. useRef를 통해 참조 객체를 생성하여 DOM 요소의 ref 속성을 통해 해당 DOM 요소를 가르키도록 설정한 후, 해당 DOM 요소의 참조 정보가 저장된 ref.current를 통해 DOM 요소의 현재 값을 읽거나 변경한다.

useRef는 useState처럼 상태를 저장하는 용도로도 사용되는데, useState는 값이 변경될 때 렌더링이 일어나지만 useRef는 값이 변경되더라도 렌더링이 일어나지 않는다. 따라서 렌더링이 필요하지 않는 상태를 관리할 때 useRef를 사용하면 불필요한 렌더링을 방지할 수 있다.

DOM 요소의 ref 속성에는 객체 외에도 Callback ref라고 부르는 콜백 함수를 전달할 수도 있다. 이 함수를 useEffect와 함께 사용하면 DOM 요소가 마운트나 언마운트될 때마다 실행될 동작을 정의할 수 있다.

나는 아래와 같이 useRef를 사용하여 DOM에 접근하였다.

import { useEffect, useRef } from 'react';
import MetaFox from '@metamask/logo';

const MetaFoxLogo = () => {
    const metaFoxRef = useRef(null);
    const isMetaFoxOn = useRef(false);

    useEffect(() => {
        if (window.document && !isMetaFoxOn.current) {
            const metaFoxInstance = MetaFox({
                pxNotRatio: true,
                width: 42.5,
                height: 42.5,
                followMouse: true,
            });

            const divMetaFox = metaFoxRef.current;

            if (divMetaFox) {
                divMetaFox.appendChild(metaFoxInstance.container);
            }

            isMetaFoxOn.current = true;
        }
    }, []);

    return <div ref={metaFoxRef} />;
};

export default MetaFoxLogo;

Ref.current를 cleanup 함수에서 사용할 시 주의할 점

useEffect(() => {
    ...

    return () => {
        delete metaFoxRef.current;
    };
}, []);

Warning

The ref value 'metaFoxRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'metaFoxRef.current' to a variable inside the effect, and use that variable in the cleanup function.

useEffect 함수의 cleanup 함수 내에서 Ref.current를 그대로 사용하면 참조된 Ref.current와 cleanup 함수의 Ref.current가 다를 가능성이 있어 경고가 발생한다.

그러므로 아래와 같이 useEffect가 실행될때의 Ref.current를 변수에 저장했다가 cleanup 함수 부분에서 사용하도록 구현한다.

useEffect(() => {
    let metaFoxRefCurrent = metaFoxRef.current;

    ...

    return () => {
        metaFoxRefCurrent = null;
    };
}, []);

그래서 최종 코드는 아래와 같이 수정되었다!

import { useEffect, useRef } from 'react';
import MetaFox from '@metamask/logo';

const MetaFoxLogo = () => {
    const metaFoxRef = useRef(null);
    const isMetaFoxOn = useRef(false);

    useEffect(() => {
        let metaFoxRefCurrent = metaFoxRef.current;

        if (window.document && !isMetaFoxOn.current) {
            const metaFoxInstance = MetaFox({
                pxNotRatio: true,
                width: 42.5,
                height: 42.5,
                followMouse: true,
            });

            const divMetaFox = metaFoxRefCurrent;

            if (divMetaFox) {
                divMetaFox.appendChild(metaFoxInstance.container);
            }

            isMetaFoxOn.current = true;
        }

        return () => {
            metaFoxRefCurrent = null;
        };
    }, []);

    return <div ref={metaFoxRef} />;
};

export default MetaFoxLogo;
profile
개발 공부💪🏼

0개의 댓글

관련 채용 정보