24. 08. 12 TIL

먼저 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