2022/01/10

devPomme·2022년 1월 10일
0

21REBIRTH

목록 보기
3/10
post-custom-banner

커스텀 드랍다운 컴포넌트의 목록 너비 설정하기

/* Dropdown.tsx */ 
import { useState, useEffect, useCallback, useRef } from 'react';
import Image from 'next/image';
/* props로 받아야하는 것: 옵션 리스트,  */

const Dropdown = (props: any) => {
  const { itemList, placeholder } = props;
  /* 드랍다운 열린 상태/닫힌 상태 (true/false) */
  const [isActive, setIsActive] = useState<boolean>(false);
  const [item, setItem] = useState<HTMLElement[] | undefined>();
  const selectedRef = useRef<HTMLDivElement>(null);

  const onActiveToggle = useCallback(() => {
    setIsActive((prev) => !prev);
  }, []);

  const onSelectItem = useCallback((e) => {
    setItem(e.target.parentNode.childNodes);
    setIsActive((prev) => !prev);
  }, []);

  const container = useRef<HTMLDivElement>(null);

  return (
    <div className="dropdown-container">
      <div
        className="select-container"
        onClick={onActiveToggle}
        ref={container}
      >
        {isActive ? (
          <>
            {item ? (
              <div className="selected">선택된 값</div>
            ) : (
              <div className="placeholder">{placeholder}</div>
            )}
            <div className="toggle-btn active" />
          </>
        ) : (
          <>
            {item ? (
              <div className="selected">선택된 값</div>
            ) : (
              <div className="placeholder">{placeholder}</div>
            )}
            <div className="toggle-btn " />
          </>
        )}
      </div>
      {isActive ? (
        /* 드랍다운 목록 태그 */
        <div
          className="option-container"
          style={
            container.current
              ? { width: container.current.clientWidth }
              : { width: '100%' }
       /* width는 부모 태그(container)의 현재 가시너비 값을 받는다. */
          }
        >
          {Object.keys(itemList[0]).includes('img')
            ? itemList.map((el, index) => {
                return (
                  <div
                    id="item"
                    className="item with-icon"
                    key={index}
                    onClick={onSelectItem}
                  >
                    <img src={el.img} width={18} height={18} />
                    <span id="item_name">{el.name}</span>
                  </div>
                );
              })
            : itemList.map((el, index) => {
                return (
                  <div
                    className="item"
                    key={index}
                    onClick={onSelectItem}
                    id="item"
                  >
                    <span id="item_name">{el.name}</span>
                  </div>
                );
              })}
        </div>
      ) : null}
    </div>
  );
};

export default Dropdown;

how to solve

➡️ useRef를 사용해 부모 태그를 참조하고, 이 부모 태그의 가시 너비를 width 속성으로 받는다.

clientWidth 속성이란?

padding을 포함한 px 단위의 요소 가시너비.
이 때 border,scrollbar, margin 은 제외한다.

참고자료
[MDN Web Docs]Element.clientWidth
[둠발롬님 - 기록은 진리다][JS] HTML 요소 크기가져오기. (offsetWidth, clientWidth, scrollWidth, getBoundingClientRect

삼항연산자 대신 함수로 탭 메뉴 구현하기

/* pointlog.tsx*/
const PointLog = () => {
    const [ActiveTab, setActiveTab] = useState('all');
  return <>
    // ...생략
     <div className="tabmenu">
            <div
              className={ActiveTab === 'all' ? `item active` : 'item'}
              onClick={(e) => setActiveTab('all')}
            >
              전체
            </div>
            <div
              className={ActiveTab === 'plus' ? `item active` : 'item'}
              onClick={(e) => setActiveTab('plus')}
            >
              적립
            </div>
            <div
              className={ActiveTab === 'minus' ? `item active` : 'item'}
              onClick={(e) => setActiveTab('minus')}
            >
              차감
            </div>
          </div></>
}

팀장님에게서 매우 심각한 삼항연산자 중독 증상을 진단받고, 함수를 실행함으로써 최대한 코드 재사용을 줄일 것을 권고받았다.

/* pointlog.tsx*/
const TabArray = [
  { eng: 'all', kor: '전체' },
  { eng: 'plus', kor: '지급' },
  { eng: 'minus', kor: '차감' },
];

const PointLog = () => {
  const [ActiveTab, setActiveTab] = useState('all');
  const createTabMenu = (array: { eng: string; kor: string }[]) => {
    return array.map((el) => {
      let className = 'item';
      if (el.eng === ActiveTab) {
        className = 'item active';
      }
      return (
        <div
          className={className}
          onClick={(e) => {
            setActiveTab(el.eng);
          }}
        >
          {el.kor}
        </div>
      );
    });
  };
  return (
  <>
    //...생략
    <div className="tabmenu">{createTabMenu(TabArray)}</div>
    </>)

기능은 똑같이 구현되면서, 코드량은 줄어들었다.
삼항연산자를 남용한 코드들은 하나하나씩 리팩토링을 해봐야겠다...

profile
헌신하고 확장하는 삶
post-custom-banner

0개의 댓글