본문 바로가기

React

컴포넌트 리팩토링 과정

 

대단한 건 아니고 개인 포르폴리오 사이트 제작 과정 중 있던 컴포넌트 리팩토링 과정을 뿌듯해서 포스팅하려 한다..

import { MenuList, MenuListItem } from 'react95'
import styled from 'styled-components'

export default function HeaderMenu() {
  return (
    <MenuListContainer inline fullWidth>
      <MenuListItem>File</MenuListItem>
      <MenuListItem>Edit</MenuListItem>
      <MenuListItem>View</MenuListItem>
      <MenuListItem>Help</MenuListItem>
    </MenuListContainer>
  )
}
const MenuListContainer = styled(MenuList)`
  margin: 0;
  box-shadow: none;
  padding: 0 4px;
  border-right: none;
  border-left: none;
`
import { MenuList, MenuListItem, Separator } from 'react95'

export default function StartMenu({ onClickStartBtn }) {
  const onClickRoutes = url => {
    window.open(url)
  }
  return (
    <MenuList
      style={{
        position: 'absolute',
        left: '0',
        bottom: '100%',
        boxSizing: 'border-box',
        margin: '0',
      }}
      onClick={onClickStartBtn}
    >
      <MenuListItem onClick={() => onClickRoutes('https://github.com/kangaroo19')}>
        <span>👿</span>
        Github
      </MenuListItem>
      <MenuListItem>
        <span role="img" aria-label="📁">
          📁
        </span>
        My account
      </MenuListItem>
    </MenuList>
  )
}

 

MenuList 컴포넌트 안에 MenuListItem 컴포넌트가 들어가는 패턴이 상당히 많이 쓰이고 있는 것을 발견하고

재사용 위해 공통 컴포넌트로 만들어 이를 리팩토링 하려 한다

  • MenuList에 프롭으로 inline,fullWidth,스타일 객체,onClick 함수가 들어가야함
  • 자식컴포넌트인 MenuListItem 컴포넌트는 동일한 패턴이므로 map함수 사용하여 반복문 돌리면 더 좋을것 같음
  • MenuListItem 컴포넌트 또한 안에 랜더링 해줄 값,onClick함수가 들어가야함
  • 반복문 돌릴 데이터는 사용하는 곳에서 프롭으로 넣어주면 될듯하다
  • 그리고 재사용하기 위해 합성 컴포넌트 패턴으로 공통 컴포넌트로 만들기

위 내용을 기반으로 새로 컴포넌트를 만들어 보았다

 

MenuListsWrapper ( MenuList에 해당)

import { MenuList } from 'react95'
import MenuListsItem from './components/MenuListsItem'

export default function MenuListsWrapper({ children, fullWidth, inline, style, onClick }) {
  return (
    <MenuList fullWidth={fullWidth} inline={inline} style={style} onClick={onClick}>
      {children}
    </MenuList>
  )
}

MenuListsWrapper.Item = MenuListsItem

자식 컴포넌트로 MenuListsItem이 들어가는 형태로 구현 

 styled-component로 만들어 관련 css 속성 값 또한 프롭으로 받을까도 생각했지만

변하는 css 속성값이 일정치 않아 스타일 객체를 받기로 결정하였다

MenuListsItem (MenuListItem에 해당)

import { MenuListItem } from 'react95'

const ItemPropTypes = PropTypes.shape({
  onClick: PropTypes.func, 
  icon: PropTypes.string, 
  title: PropTypes.string.isRequired, 
});

MenuListsItem.propTypes = {
  itemObj: ItemPropTypes.isRequired,
};

export default function MenuListsItem({ itemObj }) {
  return (
    <MenuListItem onClick={itemObj.onClick && itemObj.onClick}>
      {itemObj.icon && <span>{itemObj.icon}</span>}
      {itemObj.title}
    </MenuListItem>
  )
}

 

타입스크립트를 사용하고 있지 않으므로 후일 유지보수성을 위해 prop-types 라이브러리 사용하여 프롭의 값들을 체크해주자

Usage

import MenuListsWrapper from 'components/AppWindow/components/menuList/MenuListsWrapper'
import { useAppWindowRender } from 'utils/zustand/useAppWindowRender'

export default function StartMenu({ onClickStartBtn }) {
  const { onClickWindowOpen } = useAppWindowRender()
  const MenuListItemArr = [
    { title: 'Github', icon: '👿', onClick: () => window.open('https://github.com/kangaroo19') },
    { title: 'Login', icon: '📁', onClick: () => onClickWindowOpen('loginWindow') },
  ]

  return (
    <MenuListsWrapper style={menuListStyle} onClick={onClickStartBtn}>
      {MenuListItemArr.map(item => (
        <MenuListsWrapper.Item itemObj={item} />
      ))}
    </MenuListsWrapper>
  )
}

const menuListStyle = {
  margin: '0',
  position: 'absolute',
  left: '0',
  bottom: '100%',
  boxSizing: 'border-box',
}

 

이렇게 함으로써 공통 컴포넌트를 만들어 재사용성을 좀 더 높였다!!