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()
함수 로직은 다음과 같다.- 파라미터로 받은 문자열 key를
keys
배열에 포함되어 있는지 찾는다.
- 만약 없는 경우
keys
와dynamicRefs
배열에 추가하고 빠져나간다.
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> </> ) }