forwardRef & useImperativeHandle

kim98111·2021년 12월 7일
0

React

목록 보기
15/29
post-thumbnail

ref prop

useRef 훅으로 생성한 객체를 하위 컴포넌트에게 ref prop으로 전달하는 것은 불가능합니다. ref prop은 html 태그 이름을 JSX 문법으로 사용하는 곳에서 사용하여 html 엘리먼트를 직접 다루기 위해서 사용합니다.

만약 사용자 컴포넌트에서 ref prop을 사용하게 된다면 이는 에러가 발생하게 됩니다.

// Parent.js
import { useRef } from 'react';
import Child from './Child.js';

const Parent = () => {
    const inputRef = useRef();
    
    // ref prop으로 Child 컴포넌트에게 inputRef 전달
    // ref prop은 데이터를 전달하는 용도로 사용할 수 없음
    // 돔 노드 객체와 연결하기 위한 용도로 사용됨
    return <Child ref={inputRef} />;
};

export default Parent;
// Child.js
const Child = props => {
    return (
        <form>
            // inputRef 객체는 전달되지 않음
            <input ref={props.ref} />
        </form>
    );
};

export default Child;

ref prop은 리액트에서 "특수한 목적"으로 사용되기 때문에 일반적인 데이터 전달하는 용도로 사용할 수 없는 prop입니다.

대표적인 예로 루프를 돌면서 동일한 컴포넌트 여러 번 랜더링할 때 사용하는 key를 들 수 있습니다. ref도 마찬가지로 "HTML 엘리먼트 접근"이라는 특수한 용도로 사용되기 때문에 데이터를 전달하기 위한 일반적인 prop으로 사용을 할 수 없습니다.

forwardRef

상위 컴포넌트에서 useRef로 생성한 객체를 ref prop으로 하위 컴포넌트에게 전달하려면 react 라이브러리에서 제공하는 "React.forwardRef 라는 함수"를 사용해야 합니다.

React.forwardRef 함수의 인수로 컴포넌트 함수를 전달하면 해당 컴포넌트 함수는 첫 번째 인수로 props 객체를 전달받고, 두 번째 인수로 ref prop에 작성된 객체가 전달됩니다.

즉, 컴포넌트 함수는 props 객체상위 컴포넌트가 ref prop으로 전달한 객체를 인수로 전달받을 수 있습니다.
이를 통해 상위 컴포넌트는 하위 컴포넌트에 존재하는 엘리먼트를 조작할 수 있습니다.

import { forwardRef } from 'react';

// forwardRef의 임수로 컴포넌트를 전달
// 인수로 전달한 컴포넌트는 props객체와 ref객체를 전달받음
const Child = forwardRef((props, ref) => {
    ,,,
});

export default Child;

아래 코드처럼 Parent 컴포넌트에서 useRef 훅으로 생성한 parentRef 객체를 Child 컴포넌트로 전달하기 위해서는 "Child 컴포넌트 함수를 React.forwardRef 함수의 인수로 전달"합니다. 그리고 "Child 컴포넌트 함수는 인수로 propsref를 상위 컴포넌트로부터 전달받을 수 있습니다". Parent 컴포넌트가 ref prop으로 전달한 parentRef 객체를 Child 컴포넌트의 HTML 엘리먼트 ref prop으로 설정하여 parentRef.current에 HTML 엘리먼트를 연결시킬 수 있습니다.

// Parent.jsx
import { useRef } from 'react';

const Parent = () => {
    const parentRef = useRef();
    
    // 하위 컴포넌트에게 ref prop으로 Childref를 전달
    return <Child ref={parentRef} />;
};

export default Parent;
// Child.jsx
import { forwardRef } from 'react';

// forward 함수의 인수로 함수 컴포넌트 전달시
// 함수 컴포넌트는 props와 ref를 인수로 전달받는다
const Child = React.forwardRef((props, ref) => {
    return <input ref={ref} />;
});

export default Child;

즉, 상위 컴포넌트에서 useRef 훅으로 생성한 객체와 하위 컴포넌트의 엘리먼트와 연결하기 위해서 하위 컴포넌트 함수를 React.forwardRef 함수의 인수로 전달해야 합니다. 그리고 하위 컴포넌트 함수는 인수로 props객체와 ref를 전달받을 수 있습니다.

useImperativeHandle

// Parent.js
import { useRef } from 'react';

import Child from './child';

const Parent = () => {
    const parentRef = useRef();
    
    const clickHandler = () => {
        console.log(ref.current); // -> { clickHandler: f }
    }
    
    return (
        <>
            <Child ref={ref} />
            <button>Parent Component button</button>
        </>
    );
}

export default Parent;
// Child.js
import { forwardRef, useImperativeHandler } from 'react';

const Child = forwardRef((props, ref) => {
    const clickHandler = () => {
        console.log(ref.current); // -> { clickHandler: f }
    }

    useImperativeHandler(ref, () => {
        Console.log(ref.current); // ->DOM 요소 노드
        return { clickHandler };
        // 해당 함수 실행된 이후 ref.current는 { clickHandler: f}가 바인딩된다
    }
    
    return <button ref={ref}>Child Component button</button>;
};

export default Child;

useImperativeHandler 훅은 forwardRef와 함께 사용합니다. 상위 컴포넌트에서 useRef 훅으로 생성한 parentRef 객체를 ref prop으로 하위 컴포넌트에게 전달하고 ref prop으로 전달받는 객체를 하위 컴포넌트의 엘러먼트의 ref prop에 작성하여 서로 연결합니다.

즉, useImperativeHandler 훅은 반드시 forwardRef와 함께 사용해야 하며, forwardRef로 상위 컴포넌트에서 useRef 훅으로 생성한 객체를 하위 컴포넌트 내 리액트 엘리먼트의 ref prop과 반드시 연결해야 합니다.

하위 컴포넌트에서 useImperativeHandler훅을 사용하면 상위 컴포넌트에서 useRef 훅으로 생성한 객체의 current 프로퍼티에는 돔 요소 노드가 아닌 useImperativeHandler 훅의 두 번째 인수로 전달한 콜백 함수의 반환값에 작성된 값과 서로 연결됩니다. 즉, useImperativeHandler의 콜백함수 몸체에서만 ref.current에 돔 요소 노드가 연결되어 있으며 이후 ref.current는 콜백함수가 반환값에 작성한 값과 연결됩니다.


useImperativeHandler 훅의 첫 번째 인수로는 상위 컴포넌트에서 ref prop으로 전달한 객체를 전달하고, 두 번째 인수로는 콜백함수를 전달합니다. 이때 콜백함수내 ref.current는 돔 요소 노드와 연결되어 있습니다. 이후 콜백함수가 실행되면 ref.current는 콜백함수가 반환값으로 작성한 값과 연결됩니다.

즉, 하위 컴포넌트는 상위 컴포넌트에게 노출하고 싶은 자산들을 useImperativeHandler의 두 번째 인수인 콜백함수의 반환값인 객체에 작성하게 되면 상위 컴포넌트에서는 useRef로 생성한 객체의 current 프로퍼티를 통해 반환값에 작성된 객체에 접근할 수 있습니다.

profile
Frontend Dev

0개의 댓글