BaGyun
빠균's 개발노트
BaGyun
전체 방문자
오늘
어제
  • 분류 전체보기 (71)
    • Today I Learned (44)
      • 오류 (8)
      • JavaScript (15)
      • TypeScript (9)
      • NodeJS (0)
      • NestJS (0)
      • Database (3)
      • ORM (1)
      • 알고리즘 (2)
      • 개인공부 (4)
      • Deploy (0)
      • Git (1)
    • 개인프로젝트 (4)
    • 알고리즘 (14)
      • 프로그래머스 (14)
    • 면접질문 (5)
    • 회고 (3)

인기 글

반응형

블로그 메뉴

  • 홈
  • 태그
  • 방명록
hELLO · Designed By 정상우.
BaGyun

빠균's 개발노트

Today I Learned/개인공부

[개인공부] 2/15 React Custom Component

2022. 2. 15. 20:38

Modal

export const Modal = () => {
  const [isOpen, setIsOpen] = useState(false);

  const openModalHandler = () => {
    setIsOpen(!isOpen)
    // true가 아닌 !isOpen 으로 해준 이유는 클릭할 때마다 상태를 변환해주기 위함!
  };

  return (
    <>
      <ModalContainer>
        <ModalBtn onClick={openModalHandler}>
          {isOpen ? 'Opened!' : 'Open Modal'}          
        </ModalBtn>        
        {isOpen ? <ModalBackdrop onClick={openModalHandler}>
            <ModalView onClick={(event) => {event.stopPropagation()}}>
            <div className="close-btn" onClick={openModalHandler}>&times;</div>
            //&times는 'X'를 표현하는 HTML 엔티티(Entities)
            <div>Hello</div>
            </ModalView>
        </ModalBackdrop> : null}
      </ModalContainer>
    </>
  );
};

 

모달 창을 제외한 백그라운드를 종료하고 싶어서

<ModalBackdrop onClick={openModalHandler}> 를 입력하면 작동이 잘 되는 줄 알았으나,


어디든 클릭하면 모달창이 종료되는 현상을 볼 수 있었는데 이벤트 버블링이라하여,
간단히 설명하자면 한 요소에 이벤트가 발생하면 그 부모의 이벤트도 발생한다는 것이다.


이것을 막아주기 위해서 해당 이벤트 핸들러에 stopPropagation()를 활용해주면 된다.

 

Toggle

const ToggleContainer = styled.div`
  position: relative;
  margin-top: 8rem;
  left: 47%;
  cursor: pointer; //cursor는 종류가 다양하니 한번쯤 알아보자

  > .toggle-container {
    width: 50px;
    height: 24px;
    border-radius: 30px;
    /* background-color: #8b8b8b; */
    background-position: right;
    background: linear-gradient(to left, #8b8b8b 50%, blue 50%) right;
    background-size: 200%;
    transition: 1s;
    &.toggle--checked{
      background-position: left;
      background: linear-gradient(to right, blue 50%, #8b8b8b 50%) left;
      background-size: 200%;
      transition: 1s;
    }
  }

  > .toggle-circle {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background-color: #ffffff;
    transition : 1s;
    &.toggle--checked{
      left : 27px;
      transition : 1s;
    }
  }
`;

const Desc = styled.div`
  display: flex;
  justify-content: center;
  margin-top: 20px;
  margin-left: 15px;
`;

 

토글에선 CSS 부분에서도 배운 점이 많다.

background에 linear gradient(선형 그레디언트)는 배경 이미지에 넣는 background-image 속성에도 사용이 가능하다.

브라우저가 생성하기 때문에 이미지를 사용했을 때보다 용량이 적고, 화면을 확대해도 이미지보다 선명하게 나온다.

각도나, 방향을 설정할 수 있으며, 기본값은 위에서 아래로 설정되어 있다.

 

또한 transition으로 애니메이션을 추가하여 포인트를 줄 수 있다.

 

/* 방향 */
background:linear-gradient(to right,blue 50%,#8b8b8b 50%) //왼쪽에서 오른쪽으로
background:linear-gradient(to top right,blue 50%,#8b8b8b 50%) //상단 오른쪽으로
background:linear-gradient(to bottom right,blue 50%,#8b8b8b 50%) //하단 왼쪽으로

/* 각도 */
*deg는 degree의 약자
background:linear-gradient(45deg,blue 50%,#8b8b8b 50%) //하단 왼쪽에서 상단 오른쪽 방향(45도 방향)
background:linear-gradient(90deg,blue 50%,#8b8b8b 50%) //왼쪽에서 오른쪽 방향
/* 음수값도 가능 */
export const Toggle = () => {
  const [isOn, setisOn] = useState(false);

  const toggleHandler = () => {
    setisOn(!isOn)
  };

  return (
    <>
      <ToggleContainer onClick={toggleHandler} // 클릭 할때마다 setisOn boolen값 변환
        <div className={`toggle-container ${isOn ? "toggle--checked" : ""}`}/>
        <div className={`toggle-circle ${isOn ? "toggle--checked" : ""}`}/>
        //Toggle Switch가 On일 경우에만 표시해주기위해 삼항연산자 사용
      </ToggleContainer>
      <Desc onClick={toggleHandler}>{isOn ? 'Toggle Switch On' : 'Toggle Switch Off'}</Desc>
      //Toggle Switch가 ON인 상태일 경우에 Desc 컴포넌트 내부의 텍스트를 'Toggle Switch ON', 그렇지 않은 경우 'Toggle Switch OFF'가 된다
    </>
  );
};

 

백틱을 사용하여 Toggle Switch가 On일 경우에만 클래스네임을 각각을 따로따로 표현할 수 있는 방법을 알 수 있었다.

 

Tab

export const Tab = () => {
  // currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값을 0으로 설정한다.
  const [currentTab, setCurrentTab] = useState(0)

  const menuArr = [
    { name: 'Tab1', content: 'Tab menu ONE' },
    { name: 'Tab2', content: 'Tab menu TWO' },
    { name: 'Tab3', content: 'Tab menu THREE' },
  ];

  const selectMenuHandler = (index) => {
    setCurrentTab(index)
    //해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신되도록 함수를 완성한다.
  };

  return (
    <>
      <div>
        <TabMenu>
          {/* li 엘리먼트의 class명의 경우 선택된 tab 은 'submenu focused' 가 되며, 
                  나머지 2개의 tab은 'submenu' 가 된다. */}
                  
          {menuArr.map((el, index) => {
            return <li key={index}
                    className={`${index === currentTab ? 'submenu focused' : 'submenu'}`}
                    onClick={() => selectMenuHandler(index)}>
                      {el.name}
                    </li>
          })}
          
          {/* <li className="submenu">{menuArr[0].name}</li>
          <li className="submenu">{menuArr[1].name}</li>
          <li className="submenu">{menuArr[2].name}</li> */}
          
          //하드코딩을 하지않고, map으로 각각의 class명을 지정해줘서 관리가 가능하다.
          
        </TabMenu>
        <Desc>
          <p>{menuArr[currentTab].content}</p>
        </Desc>
      </div>
    </>
  );
};

버튼을 눌렀을 때, CSS를 활용하여 버튼을 눌렀다는 표시를 해줄 수 있으며

map()함수로 내가 누른 탭 버튼과 누르지 않은 탭버튼의

클래스를 지정해줘서 관리해줄 수 있다.

 

Tag

export const Tag = () => {
  const initialTags = ['CodeStates', 'kimcoding'];

  const [tags, setTags] = useState(initialTags);

  const removeTags = (indexToRemove) => {
    setTags(tags.filter((el) => {
      return el !== tags[indexToRemove]
    }))
    //태그를 삭제해주기 위해 filter()함수를 사용 중요**
  };
  
  const addTags = (event) => {
    // - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기
    // - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기
    // - 태그가 추가되면 input 창 비우기
    
    if(event.key === 'Enter'){
      if(event.target.value.trim() && tags.includes(event.target.value.trim()) === false){
        setTags([...tags, event.target.value])
        event.target.value = '';
      }
      else if(!event.target.value.trim()){
        event.target.value = '';
      }
      else if(tags.includes(event.target.value.trim()) === true){ // 이것까지 적어줘야 먼저 Tag되 있는 것을 똑같이 다시 적었을 때도 빈칸을 만들어준다.
      	event.target.value = '';       
      }
   }
  

  return (
    <>
      <TagsInput>
        <ul id='tags'>
          {tags.map((tag, index) => (
            <li key={index} className='tag'>
              <span className='tag-title'>{tag}</span>
              <span className='tag-close-icon'
                    onClick={() => removeTags(index)}>&times;
                {/* tag-close-icon이 tag-title 오른쪽에 x 로 표시되도록 하고,
                    삭제 아이콘을 click 했을 때 removeTags 메소드가 실행되어야 한다. */}
              </span>
            </li>
          ))}
        </ul>
        <input
          className='tag-input'
          type='text'
          onKeyUp={(event)=> {addTags(event)}} // addTags(event)를 작동시켜서 Tag를 입력
          placeholder='Press enter to add tags'
        />
      </TagsInput>
    </>

 

Tag를 만들면서 애를 많이 먹었는데 가장 큰 이유 중 하나는 addTag의 맨 마지막 줄이다.

분명 HTML에서 확인했을 땐 저 마지막 줄까지 들어가야 같은 Tag 를 입력했을 때에도 비어있는 값을 배출해 줄 수 있는데,

테스트 케이스에선 계속 오류가 났다. 다시 한번 알아보자

'Today I Learned > 개인공부' 카테고리의 다른 글

[TOOL] joi  (0) 2022.10.23
쿠키와 세션의 차이  (0) 2022.09.05
[개인공부] node.js Express 에러 핸들링  (0) 2022.07.22
    'Today I Learned/개인공부' 카테고리의 다른 글
    • [TOOL] joi
    • 쿠키와 세션의 차이
    • [개인공부] node.js Express 에러 핸들링
    BaGyun
    BaGyun

    티스토리툴바