먼저 useRef를 만들어준다.
보통 하나의 Dom 요소를 참조할 때는
const myRef = useRef<HTMLDivElement | null>(null);
이렇게 쓴다. 그럼 myRef.current는 하나의 HTMLDivElement를 참조하게 된다.
그러나 여기서는 여러 개의 HTMLDivElement를 참조해야 한다!
각 요소는 고유한 order.id를 가지고 있고, 이를 통해 각 요소를 구분할 수 있다.
const ticketContainerRef = useRef<{ [key: string]: HTMLDivElement | null }>({});
따라서, 여러 개의 요소를 관리하기 위해 객체를 사용한다.
객체는 여러 개의 키와 값 쌍을 저장할 수 있기 때문에
order.id를 키로 사용하고, 해당 HTMLDivElement를 값으로 저장할 수 있다.
예로 들면
{'order1': <div>order 1</div>}
{'order2': <div>order 2</div>}
이런 식으로 여러 개가 들어올 것이다.
그리고 여기서는 order.id라는 고유한 키로 요소를 참조해야 하기 때문에 객체가 더 적합하다.
배열은 인덱스로 요소를 참조하는데 적합함!
타입은
{ [key: string]: HTMLDivElement | null }
이렇게 쓰는데 이것은 인덱스 시그니처라고 한다
이 부분은 객체가 여러 개의 키와 값 쌍을 가질 수 있음을 의미한다.
이제 map으로 돌린 요소를 저장해야 하는데!
일단 간단하게 정리해보자면 내용은 이렇다.
먼저 tourOrders라는 3개의 주문이 있다.
그럼 map이 이 주문을 돌면서 이 주문마다 div를 만든다.
그럼 각 div는 그 order.id에 따라 ticketContainerRef.current에 저장된다.
ex. order1이라는 키로 첫번째 div 저장, order2라는 키로 두번째 div 저장...
그럼
{
"order1": <div>주문1의 내용</div>,
"order2": <div>주문2의 내용</div>,
"order3": <div>주문3의 내용</div>
}
이런 식으로 들어오는 건데, 나중에 ticketContainerRef.current['order2']이라고 하면
order2에 해당하는 div를 바로 찾아서 작업을 할 수 있게 되는 것
그럼 다시 코드를 보장
{tourOrders.map((order) => (
<div key={order.id} className='gap-4 flex flex-col'>
<div className='border-b-[1px] border-black-700 flex py-4 justify-between items-center'>
<p className='text-lg'>주문일자 {formatDate(order.pay_at)}</p>
<Link
href={/mypage/tour_orders/${order.id}}
className='flex gap-1'
>
<p className='text-sm'>상세정보</p>
<ArrowRightIcon16px />
</Link>
</div>
<div
ref={(el) => {
ticketContainerRef.current[order.id] = el;
}}
>
대충 map 돌리는 데이터가 이렇게 있고
내가 이미지로 저장하고 싶은 div에 ref를 넣어준다.
여기에 ref를 넣어주면 특정 Dom 요소를 참조하는 것이다
el은 이 div 요소를 가리키는 변수다.
리액트가 이 div를 생성할 때 이 div를 el에 전달해준다.
즉, el은 이 div 요소 자체다.
그리고
ticketContainerRef.current[order.id] = el;:
이 코드는 el (즉 해당 div 요소)를 ticketContainerRef.current 객체에 저장한다.
order.id는 각 주문마다 고유한 값이므로 이 값을 키로 사용해 각 div를 객체에 저장해두는 것이다
예를 들어, order.id가 order1이라면 이 div는 ticketContainerRef.current[order1]에 저장된다
이 작업이 끝나면 나중에 ticketContainerReft.current[order1]를 통해 order1에 해당하는 div 요소를 찾아
바로 작업할 수 있게 되는 것이다
이 과정이 모든 주문에 대해 반복되어 각 order.id와 연결된 div요소가 각각 ticketContainerRef에 저장된다!
그래서 티켓 저장 버튼을 클릭하면
그 티켓의 orderId를 전달한다.
<button
className='bg-primary-500 p-2 rounded-md w-[73px] text-xs'
onClick={() => ticketSave(order.id)}
>
티켓 저장
</button>
그럼
const ticketSave = useCallback((orderId: string) => {
setShowText(false);
const ticketContainer = ticketContainerRef.current[orderId];
if (ticketContainer) {
toPng(ticketContainer)
.then((dataUrl) => {
const link = document.createElement('a');
link.download = `${orderId}.png`;
link.href = dataUrl;
link.click();
})
.then(() => {
setShowText(true);
})
.catch((err) => {
throw err;
});
}
}, []);
orderId를 받아와서 ticketContainerRef.current에서 orderId에 해당하는 값을 찾으면
그 해당하는 Dom 요소를 가져오는 것이다
** useRef는 기본적으로 하나의 객체를 반환한다.
이 객체는 { current: null } 처럼 생겼다.
이 객체의 current라는 속성에 우리가 저장하고 싶은 값이나 DOM 요소를 넣을 수 있다.
'TIL' 카테고리의 다른 글
24. 08. 14 TIL (0) | 2024.08.14 |
---|---|
24. 08. 13 TIL (0) | 2024.08.13 |
24. 08. 09 TIL (0) | 2024.08.09 |
24. 08. 08 TIL (0) | 2024.08.08 |
24. 08. 07 TIL (0) | 2024.08.07 |