useRef Hook 동적 할당하기

useRef Hook 동적 할당하기

프로젝트
프로젝트
카테고리
Dev
작성일
2024-03-14
태그
TIL
작성자
꾸생
상태
공개
useRef는 리액트 컴포넌트 안에서 렌더링이 필요하지 않은 값을 참조할 때 사용하는 훅(Hook)이다. 보통 DOM Element 또는 단순 변수를 할당해 사용한다. 이전 리액트에서 let 변수 대신 useRef 를 왜 사용하는지 포스팅을 했는데, 이번엔 다른 포스팅 소재가 생겼다.
🙄
🙄

🔥 반복되는 Element 요소에 동적 Ref 할당

개인적으로 리액트에서 useRef에 단일 Element 요소만 할당해 사용했다. Vue 에서는 동적 할당을 해본 적 있지만 왜 리액트에선 처음인지 의문이다. 아무튼 이번 목표는 동적으로 useRef를 할당하기 위해 커스텀 훅을 작성해 쉽게 반복되는 컴포넌트 요소에 ref를 선언할 것이다.

🐶 반복되는 컴포넌트

import { KButton } from 'kku-ui' const items = [ { title: 'Not', value: '1' }, { title: 'your', value: '2' }, { title: 'keys', value: '3' }, { title: 'not', value: '4' }, { title: 'your', value: '5' }, { title: 'Bitcoin', value: '6' }, ] export default function Component() { return ( <> {items.map((item) => (<KButton key={item.value}>{item.title}</KButton>)} </> ) }
items 배열 요소만큼 <KButton/>컴포넌트를 렌더링하는 코드다. (v-for와 동일) <KButton/>은 내가 개발하고 있는 kku-ui 라이브러리인데 깨알 자랑해본다.
items는 6개에 객체가 있으니 6개 ref를 만들어 줘야 한다. 이름은 useDynamicRef가 제격이다 🙄

📝 useDynamicRef Hook 작성

 

📌 1. Hook 뼈대 작성

export default function useDynamicRef<T>() { ... }
코드 수를 줄이기 위해 화살표 함수가 아닌 function 키워드로 정의하고 esm 내보내기를 시전 한다. 타입은 제네릭<T>으로 사용하는 쪽에서 직접 타입을 정의하게 해 내 일감을 쳐낸다.

📌 2. 변수 선언

import { useRef } from 'react' export default function useDynamicRef<T>() { const dynamicRef = useRef<{T[]}>({}); const keys = useRef<string[]>([]); ... return { dynamicRef }; }
제네릭으로 받은 타입이 배열 형식으로 담기는 dynamicRef를 선언했다. 기존에는 객체 형태로 만들었는데, 주로 배열로 많이 다뤘기 때문에 배열 형태로 재작성 했다. 그래서 배열에 담길 때 순서를 보장시켜주기 위해 keys 라는 배열도 선언해 줬다.

📌 3. 함수 작성

import { useRef, useCallback } from 'react' export default function useDynamicRef<T>() { const dynamicRef = useRef<{T[]}>({}); const keys = useRef<string[]>([]); const handleRef = useCallback((key: string) => { return (ref: T) => { const keyIdx = keys.current.indexOf(key); if (keyIdx !== -1) { dynamicRef.current[keyIdx] = ref; return; } keys.current.push(key); dynamicRef.current.push(ref); }; }, []); ... ... }
handleRef함수는 반복되는 ref를 초기화하는 역할을 한다. 파라미터로 할당할 객체 key값을 받는데, 보통 리액트에서 반복되는 컴포넌트를 map() 함수로 돌릴 때 사용하는 key 값으로 동일하게 사용하면 된다.
handleRef() 함수 로직은 다음과 같다.
  1. 파라미터로 받은 문자열 key를 keys 배열에 포함되어 있는지 찾는다.
  1. 만약 없는 경우 keysdynamicRefs 배열에 추가하고 빠져나간다.
  1. keys 배열에 포함된 경우 해당 key 인덱스 순서에 맞게 dynamicRefs에 값을 초기화 한다.

📌4. 최종 작성된 코드

import { useRef, useCallback } from 'react' export default function useDynamicRef<T>() { const dynamicRefs = useRef<{T[]}>([]); const keys = useRef<string[]>([]); const handleRef = useCallback((key: string) => { return (ref: T) => { const keyIdx = keys.current.indexOf(key); if (keyIdx !== -1) { dynamicRef.current[keyIdx] = ref; return; } keys.current.push(key); dynamicRefs.current.push(ref); }; }, []); return { refs: dynamicRefs.current, handleRef }; }
마지막 return 에서는 refs라는 이름으로 dynamicRefs.current 로 반환해주는 센스 😎

✅ useDynamicRef 사용하기

import { KButton, KButtonRefs } from 'kku-ui' import useDynamicRef from '@/lib/useDynamicRef' const items = [ { title: 'Not', value: '1' }, { title: 'your', value: '2' }, { title: 'keys', value: '3' }, { title: 'not', value: '4' }, { title: 'your', value: '5' }, { title: 'Bitcoin', value: '6' }, ] export default function Component() { const { refs: buttonRefs, handleRef } = useDynamicRef<KButtonRefs>(); const onClick = () => { console.log(buttonRefs) buttonRefs.forEach((buttonRef) => { buttonRef.startLoading(); }) } return ( <> {items.map((item) => (<KButton key={item.value}>{item.title}</KButton>)} <KButton onClick={onClick}>Test</KButton> </> ) }

🕺🏻 테스트

notion image