React - Lucide React, Radix Icon, 아이콘 컴포넌트처럼 사용하기

Moolbum·2023년 11월 19일
3

React

목록 보기
23/23
post-thumbnail

React의 다양한 라이브러리 중에 아이콘을 편하게 사용할 수 있도록 지원해 주는 라이브러리가 있어요!
Lucide React, Radix Icon을 컴포넌트처럼 사용하는 방법을 알려드릴게요.

현재 맞닥뜨린 상황

아이콘을 사용할 때마다 아이콘의 이름을 매번 import 하는 것이 너무 싫었어요.
하나의 컴포넌트에서 다양한 아이콘을 사용한다면 import 한 아이콘들의 이름이 길어질 상황이 예상되었어요.

  • lucide 공식문서 예시코드
import { Camera } from 'lucide-react';

const App = () => {
  return <Camera color="red" size={48} />;
};

export default App;
  • Radix Icon 공식문서 예시코드
import { FaceIcon, ImageIcon, SunIcon } from '@radix-ui/react-icons'

function MyComponent () {
  return (
    <div>
      <FaceIcon />
      <SunIcon />
      <ImageIcon />
    </div>
  )
}

해결방법

분명히 저처럼 생각하는 사람들이 있을것 같았고 공식 라이브러리에서 지원을 해줄 것이라고
생각하고 공식문서를 더 찾아봤어요 역시 컴포넌트처럼 사용할 수 있는 방법이 있었어요
atom 폴더에 icons라는 폴더를 만들고 세 가지의 파일을 만들었어요.

LocalIcon.tsx : 로컬의 이미지를 이용한 아이콘 컴포넌트
LucideIcon.tsx : LucideIcon 컴포넌트
RadixIcon.tsx : RadixIcon 컴포넌트

LocalIcon.tsx

className으로 스타일을 주고 있는 건 이번 프로젝트에서는 tailwindCSS를 적용해서 className으로 스타일을 주고 있어요

import { HTMLAttributes } from 'react';

export interface LocalIconProps extends HTMLAttributes<HTMLSpanElement> {
  iconSrc: string;
  iconSize?: number;
  onClick?: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void;
  className?: string;
  onPointerDown?: (e: React.PointerEvent<HTMLSpanElement>) => void;
}

const LocalIcon = ({
  iconSrc,
  iconSize = 16,
  onClick,
  onPointerDown,
  className,
  ...props
}: LocalIconProps) => {
  const defaultStyle: React.CSSProperties = {
    display: 'inline-block',
    flexShrink: 0,
    background: `url(${iconSrc}) center center/contain`,
    backgroundRepeat: 'no-repeat',
    width: `${iconSize}px`,
    height: `${iconSize}px`,
  };

  return (
    <span
      className={className}
      onClick={onClick}
      onPointerDown={onPointerDown}
      style={
        onClick || onPointerDown
          ? { cursor: 'pointer', ...defaultStyle }
          : { ...defaultStyle }
      }
      {...props}
    />
  );
};

export default LocalIcon;

LucideIcon.tsx

import { cn } from '@/src/lib/utils';
import colorSet, { ColorType } from '@/src/styles/color';
import { icons } from 'lucide-react'; <------ lucide import
import { HTMLAttributes } from 'react';

export interface LucideIconProps extends HTMLAttributes<HTMLOrSVGElement> {
  name: keyof typeof icons;
  color?: ColorType;
  size?: number;
}

const LucideIcon = ({
  name,
  color = 'gray9',
  size = 16,
  ...props
}: LucideIconProps) => {
  const SelectLucideIcon = icons[name];

  const isClickEvent = !!props.onClick;
  const pointerStyle = isClickEvent ? 'cursor-pointer' : '';

  return (
    <SelectLucideIcon
      color={colorSet[color]}
      size={size}
      className={cn(pointerStyle, props.className)}
      {...props}
    />
  );
};

export default LucideIcon;

RadixIcon.tsx

import React from 'react';
import * as icons from '@radix-ui/react-icons'; <------ Radix Icon 컴포넌트 import
import { IconProps } from '@radix-ui/react-icons/dist/types';
import { cn } from '@/src/lib/utils';
import colorSet, { ColorType } from '@/src/styles/color';

export interface RadixIconProps extends IconProps {
  name: keyof typeof icons;
  size?: number;
  color?: ColorType;
}

function RadixIcon({
  name,
  size = 16,
  color = 'gray9',
  ...props
}: RadixIconProps) {
  const SelectRadixIcon = icons[name];

  const isClickEvent = !!props.onClick;
  const pointerStyle = isClickEvent ? 'cursor-pointer' : '';

  return (
    <SelectRadixIcon
      width={size}
      height={size}
      className={cn(pointerStyle, props.className)}
      {...props}
      color={colorSet[color]}
    />
  );
}

export default RadixIcon;

실제 사용 예시

LucideIcon, RadixIcon 컴포넌트 사용 시에 이름값을 추론해 주어서 편하게 사용할 수 있어요

LocalIcon

<LocalIcon
    iconSrc={ClearSvg}
    iconSize={16}
    onPointerDown={() => {
    onClear?.();
    setValue('');
    }}
/>

LucideIcon

<LucideIcon
    size={16}
    name="Calendar"
    color={triggerProps?.disabled ? 'gray6' : 'gray9'}
    className="flex flex-shrink-0"
/>

RadixIcon

<RadixIcon
    size={16}
    color="gray5"
    name="InfoCircledIcon"
    className="flex flex-shrink-0"
/>
profile
Junior Front-End Developer 👨‍💻

0개의 댓글