컴포넌트를 통해 자식 중 하나에
ref를 자동으로 전달하는 기법
forwardRef로 감싸주면 ref 사용 가능DOM element에 접근하고 싶을 때 사용1. 부모 컴포넌트에서 useRef() 선언 -> 자식 컴포넌트에 보냄
import { useRef } from 'react'
import Child from './Child'
const Parent = () => {
const compRef = useRef()
return (
<Child ref={compRef} />
)
}
export default Parent;
2. 자식 컴포넌트를 forwardRef()로 감싸고 부모에서 사용할 함수를 useImperativeHandle()로 감싼다.
import { forwardRef, useImperativeHandle } from 'react'
const Child = forwardRef((props, ref) => {
// 부모 컴포넌트에서 사용할 함수 설정
useImperativeHandle(ref, () => ({
req1,
req2
}))
// 함수 1
const req1 = () => { }
// 함수 2
const req2 = () => { }
}
export default Child
3. 부모 컴포넌트에서 current 프로퍼티를 통해 함수 사용
import { useRef } from 'react'
import Child from './Child'
const Parent = () => {
const compRef = useRef()
const fnReqBtn1 = e => {
e.preventDefault();
compRef.current.req1();
}
const fnReqBtn2 = e => {
e.preventDefault();
compRef.current.req2();
}
return (
<>
<Child ref={compRef} />
<Link onClick={fnReqBtn1}>가입버튼1</Link>
<Link onClick={fnReqBtn2}>가입버튼2</Link>
</>
)
}
export default Parent;
-> 자식 컴포넌트를 forwardRef로 감쌌을 때, 2번째 매개변수 ref는 부모 컴포넌트가 props로 넘긴 값 = compRef
React에서 부모 컴포넌트가 자식 컴포넌트의 내부 메서드나 상태를 직접 호출할 수 있도록 하는 Hook
- 보통
forwardRef와 함께 사용
ref가 클래스 컴포넌트에 사용되는 것과 유사하게, 내부 메소드를 외부에 보낼 수 있습니다.
부모에게 꼭 자식의 실제 reference를 보내지 않고 우리가 원하는 일종의 proxy, reference를 보내는게 가능하므로 컴포넌트간 독립성을 보장할 수 있습니다.
1. 부모 컴포넌트에서 Ref 선언 -> 인스턴스 생성할 컴포넌트에 전달
export default function App() {
const ref = useRef(null);
console.log("parent");
return (
<div className="App">
<p>Parent</p>
<button>Click</button>
<Input ref={ref} />
</div>
);
}
-> 이 때 자식 컴포넌트인 Input은 forwardRef를 통해 전달된 ref 획득
2. useImperativeHandle 훅을 사용해 인스턴스 생성
// Input Components
import { forwardRef, useImperativeHandle, useRef } from "react";
const Input = (props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(
ref,
() => ({
myFocus: () => inputRef.current.focus(),
}),
[ref]
);
return <input placeholder="Child Input" {...props} ref={inputRef} />;
};
export default forwardRef(Input);
-> 부모 컴포넌트인 App에서 InputRef를 다룰 수 있는 메서드 생성
3. 부모 컴포넌트에서 메서드 접근
// App Components
export default function App() {
const ref = useRef(null);
const handleClickFocus = () => {
ref.current.myFocus();
};
return (
<div className="App">
<p>Parent</p>
<button onClick={handleClickFocus}>Click</button>
<Input ref={ref} />
</div>
);
-> 자식 컴포넌트의 DOM 노드 접근 가능
// 모달을 컨트롤할 함수들을 정의
export type ModalRef<T> = {
close: () => void;
open: (data: T) => void;
};
// 모달 컴포넌트의 타입
type Props<T> = {
children: (data: T) => React.ReactNode;
};
const Modal = forwardRef<ModalRef<any>, Props<any>>(({ children }, ref) => {
const [isOpen, setIsOpen] = useState(false);
const [data, setData] = useState<any>(null);
// 모달 컨트롤 함수 정의
useImperativeHandle(ref, () => ({
close: () => {
setIsOpen(false);
},
open: (data: any) => {
// any 사용 또는 명시적인 제네릭 타입
setIsOpen(true);
setData(data);
},
}));
if (!isOpen) return null;
return createPortal(
<div className="fixed inset-0 flex items-center justify-center z-50">
<div className="absolute inset-0 bg-black opacity-40" />
<div className="bg-white p-4 rounded-md relative z-10">
{data && children(data)}
</div>
</div>,
document.body
);
});
Modal.displayName = "Modal";
export default Modal;
ref 사용은 지양할 것ref는 props가 수행할 수 없는 명령어를 처리하는 용도로만 사용현재 컴포넌트를 다른 DOM으로 다른 위치에 렌더링할 수 있도록 해주는 기능
import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
const Modal = ({ isOpen, onClose }) => {
const [portalRoot, setPortalRoot] = useState(null);
useEffect(() => {
let root = document.getElementById("portal-root");
if (!root) {
root = document.createElement("div");
root.id = "portal-root";
document.body.appendChild(root);
}
setPortalRoot(root);
}, []);
if (!isOpen || !portalRoot) return null;
return createPortal(
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white p-6 rounded-lg">
<h2>모달 창</h2>
<button onClick={onClose}>닫기</button>
</div>
</div>,
portalRoot
);
};
export default Modal;
-> createPortal(children, container)
- children: 렌더링할 요소 (컴포넌트 내용)
- container: 해당 요소를 삽입할 DOM 노드
-> useEffect를 사용해 portal-root라는 <div>를 동적으로 생성하고 document.body에 추가
-> createPortal을 이용해, 모달을 portal-root에 렌더링
- 원래 부모 컴포넌트의 DOM 구조와 상관없이 body 아래에서 렌더링