react-Icons 공식 문서를 보면서 작업을 하던 도중 효율적으로 사용하기 위한
내가 시도한 방법에 대해서 정리 해보려고 한다.
import { IconName } from 'react-icons/IconCategories'
위와 같이 아이콘 이름 + 아이콘의 카테고리를 정해서 import
를 하는데 뭔가 불편했고,
아이콘이 한두개면 괜찮지만 아이콘이 많아질수록 쌓여가는 아이콘 관련 import
가 너무 불편했다.
다른 사람들은 어떻게 사용할까 하면서 찾아봤는데 원하는 답을 얻지는 못하였다
넵!
react-icons
를 사용하기 위해 설치를 해보자$ npm install react-icons
나는 Font Awesome6를 사용하기로 했고, 아이콘들의 리스트를 정했다.
프로젝트마다 폴더 구조도 다르겠지만 나의 구조는 각각의 컴포넌트들의
기본 스타일 + 전체적으로 쓸 컬러 + 각각의 규칙을 정하여 theme 폴더에서 관리를 했고,
여기에 iocn를 추가하여 작성하였다
// themes/default/components/icon.js
import { FaAngleLeft, FaAngleRight, FaAngleUp, FaAngleDown, FaXmark, FaRegCircleXmark, FaPlus, FaMagnifyingGlass, FaBars, FaEye, FaEyeSlash, FaRotateRight, FaGear } from 'react-icons/fa6';
const baseStyle = {
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
};
const iconTypes = {
arrowLeft: FaAngleLeft,
arrowRight: FaAngleRight,
arrowUp: FaAngleUp,
arrowDown: FaAngleDown,
close: FaXmark,
closeFill: FaRegCircleXmark,
plus: FaPlus,
search: FaMagnifyingGlass,
hamburger: FaBars,
visible: FaEye,
unvisible: FaEyeSlash,
refresh: FaRotateRight,
system: FaGear,
};
const iconSizes = {
lg: {
width: '24px',
height: '24px',
},
md: {
width: '20px',
height: '20px',
},
sm: {
width: '16px',
height: '16px',
},
};
export default { baseStyle, iconTypes, iconSizes };
// themes/default/index.js
import components from './components';
import tokens from './tokens';
export default {
...tokens,
components,
};
// themes/index.js
export { default as defaultTheme } from './default';
deafultTheme
로 묶어 export를 해준다.import { createContext, useContext } from 'react';
import { defaultTheme } from '../themes';
import PropTypes from 'prop-types';
const ThemeContext = createContext();
// eslint-disable-next-line react-refresh/only-export-components
export const useTheme = () => {
return useContext(ThemeContext);
};
export const ThemeProvider = ({ children }) => {
return <ThemeContext.Provider value={defaultTheme}>{children}</ThemeContext.Provider>;
};
ThemeProvider.propTypes = {
children: PropTypes.node,
};
defaultTheme
를 다른 컴포넌트에서useTheme
를 사용하여 쓸 수 있게 된다.// App.js
import { ThemeProvider } from './theme/ThemeContext';
function App() {
return (
<ThemeProvider>
{/* 각각의 프로젝트에 맞는 구성 */}
</ThemeProvider>
);
}
export default App;
ThemeProvider
를 통해 전역 설정을 해준다.가장 먼저 내가 고려한 것은 아이콘을 유동적으로 사용하기 위함이고 그게 아니였다면
그냥 주먹구구식 import를 사용해서 썼을 것이다.
컴포넌트를 사용하는게 편하게 사용하기 위함인데 너무 불편했었다.
나만의 Icon컴포넌트 emotion과 함께 응용하여 만들어보자
// components/Icon/Icon.jsx
import styled from '@emotion/styled'
import { useTheme } from '../../../theme/ThemeContext';
const StyledIcon = styled.span`
${(props) => props.theme.components.icon.baseStyle}
.Icon {
${(props) => props.theme.components.icon.iconSizes[props.size]}
color:${(props) => props.theme.colors[props.color]};
}
`;
react-icons
를 통해 컴포넌트를 사용하면 svg
태그를 단독으로 렌더링 해주는데,
이거는 조금 별로 안 좋은 방법이고 입맛대로 수정하기 위해선 감싸는 태그가 하나 필요하기에
span
를 통해 스타일을 해준다.
아이콘들에 따라 import
를 하기 싫기에 name + size + color를 통해 나만의 컴포넌트로 설정해보았다.
해당 프로젝트에서 하나하나 width=""를 지정하기엔 너무 번거로우니 프로젝트의 일관성을 위해
정해진 size, color를 props를 받을 수 있게 한다.
// components/Icon/Icon.jsx
const Icon = ({ name, size, color }) => {
const PREFIX = 'Icon';
const theme = useTheme();
const SelectedIcon = theme.components.icon.iconTypes[name];
return (
<StyledIcon size={size} theme={theme} color={color}>
<SelectedIcon className={PREFIX} />
</StyledIcon>
);
};
Icon.defaultProps = {
size: 'md',
color: 'black',
};
Icon.propTypes = {
name: PropTypes.string.isRequired,
size: PropTypes.oneOf(['lg', 'md', 'sm']),
color: PropTypes.string,
};
export default Icon;
물론 이것보다 폴더구조를 간단하게 해서 iocn파일을 만들고 거기에서 icon 리스틀 정하고
Icon컴포넌트에 icon 리스트를 import를 해도 된다
하지만 정답은 없고 이러한 방법도 해보고 저러한 방법도 해봐야 어떤게 좋고 나쁨을 구분할 수 있기에
나는 전체적인 기본 스타일을 관리하는 파일이 있었으면 하는 생각에 이러한 구조를 선택하게 되었다.
기본 스타일을 위한 다른 코드들은 생략을 한 점은 양해를 구한다.
// pages/components
import Icon from '../../components/hds/Icon/Icon';
const TestPages = () => {
const theme = useTheme();
return (
<div>
<StyledPreview theme={theme}>
{Object.keys(iconMap).map((name, idx) => (
<IconPreview key={idx} className="IconPreview">
<Icon name={name} color={randomColor} />
<p>{name}</p>
</IconPreview>
))}
</StyledPreview>
</div>
);
};
export default TestPages;
아주 잘 나옵니다 구우우욷
정답은 아니지만 확실히
import
줄이 한줄로 끝이 났고 필요할 때 마다 유동적으로 쓸 수 있게되었다.
모든 질문 의견 환영합니다.