- Today
- Total
빵 입니다.
forwardRef()와 useImperativeHandle() 본문
🔥함수형 컴포넌트 기반으로 정리를 했습니다!
하위 컴포넌트 안에 있는 요소에 직접 접근하고 싶은데
컴포넌트에 ref 속성을 추가해 조작하려 하면 null 값을 참조하려 한다는 오류가 뜬다.
=> 함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에서 ref 어트리뷰트를 사용할 수 없다.
그럼 ref를 통해 함수 컴포넌트를 직접 제어하려면 어떻게 해야하지?
❗️forwardRef()를 사용하면 된다.
forwardRef()는 부모 컴포넌트에서 전달받은 ref를 자식 컴포넌트로 전달하는 역할을 한다.
(부모 컴포넌트의 ref와 실제 사용할 자식 컴포넌트의 ref 연결)
자식 컴포넌트는 전달받은 ref를 HTML 요소의 속성으로 넘겨줌으로써 함수 컴포넌트 역시 ref를 통한 제어가 가능해진다.
🌀ref
React에서 ref는 HTML DOM 요소에 직접 접근하기 위해서 사용한다.
<input type="text" ref={inputRef} />
🌀forwardRef()
HTML DOM 요소가 아닌 React 컴포넌트에서 ref를 사용하려면 React에서 제공하는 forwardRef()를 사용해야 한다.
// [부모 컴포넌트] Form.js
import React, { useRef } from ‘react’;
const Form = () => {
const inputRef = useRef(null);
const focusHandler = (event) => {
inputRef.current.focus();
};
return (
<Input ref={inputRef} />
<button onClick={focusHandler}>클릭 시 포커싱</button>
);
};
export default Form;
// [자식 컴포넌트] Input.js
import React, { forwardRef } from ‘react’;
const Input = forwardRef((props, ref) => {
return <input type="text" ref={ref} />;
});
export default Input;
- 컴포넌트를 forwardRef()로 감싸서 내보내야 한다.
- forwardRef()의 인수는 컴포넌트 함수이고, 그 컴포넌트 함수가 부모 컴포넌트에서 전달한 props와 ref 값을 받는다.
- forwardRef()는 리액트 컴포넌트를 반환하는데, 자식 컴포넌트인 Input은 ref에 바인딩될 수 있는 리액트 컴포넌트이다.
부모 컴포넌트에서 자식 컴포넌트의 메서드나 핸들러를 사용하고 싶을 땐 어떻게 해야할까?
❗️forwardRef()와 useImperativeHandle() 훅을 사용한다!
클래스 컴포넌트에선 ref만 사용해서 부모 컴포넌트에서는 자식 컴포넌트 전체가 아닌 해당 컴포넌트 인스턴스에 직접 접근할 수 있는데, 함수형 컴포넌트는 인스턴스가 없기 때문에 ref로 속성, 메서드를 노출하는 것이 불가능하다.
대신 forwardRef()와 useImperativeHandle()을 사용할 수 있다.
1. forwardRef를 사용하여 부모 컴포넌트에서 자식 컴포넌트의 ref에 접근한다.
2. useImperativeHandle()를 사용하여 자식 컴포넌트의 속석이나 메서드를 외부로 노출시킨다.
🌀useImperativeHandle() 훅은 세개의 매개변수를 받는다.
- 첫번째 매개변수는 외부에서 전달되는 ref 객체
=== forwardRef()의 인자 함수의 두번째 인자인 ref - 두번째 매개변수는 ref를 통해 접근 가능한 메서드나 속성을 반환하는 함수
이 함수가 반환하는 값들이 실제로 부모 컴포넌트에서 접근 가능하다.
함수는 컴포넌트가 렌더링될 때마다 호출되므로 매 렌더링마다 같은 값을 반환하거나 변경된 값을 반환할 수 있다. - 세번째 매개변수는 의존성 배열인데, 선택적으로 추가한다.
이 배열에 포함된 값들이 변경될 때만 함수가 다시 호출된다. 생략한다면 렌더링될 때마다 함수가 호출된다.
// [부모 컴포넌트] Parent.js
import React, { useRef } from 'react';
import Child from './Child';
const Parent = () => {
const myRef = useRef();
const handleButtonClick = () => {
// 자식 컴포넌트에서 useImperativeHandle() 훅을 사용해 노출시킨 속성과 메서드
myRef.current.increment();
const currentCount = myRef.current.getCount();
};
return (
<>
<Child ref={myRef} />
<button onClick={handleButtonClick}>자식 컴포넌트의 메서드 실행</button>
</>
);
};
export default Parent;
// [자식 컴포넌트] Child.js
import React, { forwardRef, useImperativeHandle, useState } from 'react';
const Child = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
// useImperativeHandle을 사용하여 외부로 노출할 메서드를 설정
useImperativeHandle(ref, () => ({
increment,
getCount: () => count,
}), [count]); // count가 변경될 때만 함수가 호출되도록 함
return (
<>
<p>카운트: {count}</p>
</>
);
});
export default Child;
'프론트엔드 > React' 카테고리의 다른 글
useCallback() (0) | 2024.01.04 |
---|---|
React.memo() (0) | 2024.01.04 |
Hooks의 규칙 (0) | 2023.12.14 |
리액트의 Context ➡️ Context API (0) | 2023.12.14 |
리액트 주요 콘셉트 (0) | 2023.12.13 |