-
[TIL] 2022.2.22.(수)코드스테이츠 43기 2023. 2. 22. 16:51
React-custom-component과제
modal
import { useState } from "react"; import styled from "styled-components"; export const ModalContainer = styled.div` display: flex; justify-content: center; align-items: center; height: 100%; // TODO : Modal을 구현하는데 전체적으로 필요한 CSS를 구현합니다. `; export const ModalBackdrop = styled.div` // TODO : Modal이 떴을 때의 배경을 깔아주는 CSS를 구현합니다. background-color: rgba(0, 0, 0, 0.5); position: fixed; left: 0; right: 0; top: 0; bottom: 0; display: flex; justify-content: center; align-items: center; `; export const ModalBtn = styled.button` background-color: var(--coz-purple-600); text-decoration: none; border: none; padding: 20px; color: white; border-radius: 30px; cursor: grab; `; export const ModalView = styled.div.attrs((props) => ({ // attrs 메소드를 이용해서 아래와 같이 div 엘리먼트에 속성을 추가할 수 있습니다. role: "dialog", }))` // TODO : Modal창 CSS를 구현합니다. background-color: white; width: 300px; height: 150px; display: flex; flex-direction: column; display: flex; justify-content: center; align-items: center; border-radius: 30px; > button { } `; export const Modal = () => { const [isOpen, setIsOpen] = useState(false); const openModalHandler = (event) => { setIsOpen(!isOpen); //ModalBtn 클릭시 발생되는 change 이벤트 핸들러 //클릭할때마다 상태가 Boolean값으로 변경된다. // TODO : isOpen의 상태를 변경하는 메소드를 구현합니다. }; return ( <> <ModalContainer> <ModalBtn onClick={openModalHandler} // TODO : 클릭하면 Modal이 열린 상태(isOpen)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다. > Open Modal {/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때는 ModalBtn의 내부 텍스트가 'Opened!' 로 Modal이 닫힌 상태(isOpen이 false인 상태)일 때는 ModalBtn 의 내부 텍스트가 'Open Modal'이 되도록 구현해야 합니다. */} </ModalBtn> {/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때만 모달창과 배경이 뜰 수 있게 구현해야 합니다. */} {isOpen ? ( <ModalBackdrop onClick={openModalHandler}> <ModalView onClick={(e) => e.stopPropagation()}> <button onClick={openModalHandler}>X</button> <div>43기 화이팅!</div> </ModalView> </ModalBackdrop> ) : null} </ModalContainer> </> ); }; //자식요소에서 이벤트가 발생했을 때, //부모요소에서도 같은 이벤트가 발생한것처럼 동작 //=>이벤트 버블링 // 막기 위해서는? event => event.stopPropagation()
-click할때마다 바뀌는 함수를 구현해주기 위해서 setIsOpen(!isOpen)을 해주었다. isOpen의 반대 boolean값으로 변경된다.
-이후isOpen이 true인 상태에만 랜더링하는 함수를 만들었다.(삼항연산자 이용)
-부모요소에서 이벤트가 발생했을때, 자식요소에서 이벤트가 발생하는것이 이벤트 버블링인줄 알았으나... 자식요소에서 이벤트가 발생했을때, 그 상위요소까지 이벤트가 실행되는 현상이 이벤트 버블링이다. 이걸 막기 위해서 stop.Propagation을 이용하면 된다.
-모달구현이 너무 어렵고 어떻게 접근할지 감이 오지 않았는데... 수업들으면서 따라쳐보니까 그 다음것들은 어렵지 않게 풀었다!
Tab
import { useState } from 'react'; import styled from 'styled-components'; const TabMenu = styled.ul` background-color: #dcdcdc; color: rgba(73, 73, 73, 0.5); font-weight: bold; display: flex; flex-direction: row; justify-items: center; align-items: center; list-style: none; margin-bottom: 7rem; .submenu { width: 100%; padding: 15px 10px; cursor: pointer; } .focused { background-color: #4000c7; color: rgba(255, 255, 255, 1); transition: 0.3s; } & div.desc { text-align: center; } `; const Desc = styled.div` text-align: center; `; export const Tab = () => { 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); }; return ( <> <div> <TabMenu> {menuArr.map((ele, index) => { return ( <li key={index} className={currentTab === index ? 'submenu focused' : 'submenu'} onClick={() => selectMenuHandler(index)} > {ele.name} </li> ); })} </TabMenu> <Desc> <p>{menuArr[currentTab].content}</p> </Desc> </div> </> ); };
-탭은 어렵지 않게 구현했던것같다.
-다만, text(Desc 컴포넌트 안의 내용)구현할때 filter쓰고 맵쓰고 난리 쳤는데... 그냥 menuArr[currentTab].content 하면 되는거였다 ㅋㅋ
Tags
import { useState } from "react"; import styled from "styled-components"; // TODO: Styled-Component 라이브러리를 활용해 여러분만의 tag 를 자유롭게 꾸며 보세요! export const TagsInput = styled.div` margin: 8rem auto; display: flex; align-items: flex-start; flex-wrap: wrap; min-height: 48px; width: 480px; padding: 0 8px; border: 1px solid rgb(214, 216, 218); border-radius: 6px; > ul { display: flex; flex-wrap: wrap; padding: 0; margin: 8px 0 0 0; > .tag { width: auto; height: 32px; display: flex; align-items: center; justify-content: center; color: #fff; padding: 0 8px; font-size: 14px; list-style: none; border-radius: 6px; margin: 0 8px 8px 0; background: var(--coz-purple-600); > .tag-close-icon { display: block; width: 16px; height: 16px; line-height: 16px; text-align: center; font-size: 14px; margin-left: 8px; color: var(--coz-purple-600); border-radius: 50%; background: #fff; cursor: pointer; } } } > input { flex: 1; border: none; height: 46px; font-size: 14px; padding: 4px 0 0 0; :focus { outline: transparent; } } &:focus-within { border: 1px solid var(--coz-purple-600); } `; export const Tag = () => { const initialTags = ["CodeStates", "kimcoding"]; const [tags, setTags] = useState(initialTags); const removeTags = (indexToRemove) => { console.log(indexToRemove); setTags(tags.filter((el, index) => index !== indexToRemove)); // TODO : 태그를 삭제하는 메소드를 완성하세요. }; const addTags = (event) => { let value = event.target.value; if (!tags.includes(value) && value !== "") { setTags([...tags, value]); //push를 하면 안되는 이유는 리액트는 주소값이 바뀌어야 변한것을 인지하기 때문에.!(참조형 데이터) //원시형데이터는 상관없음. console.log(tags); event.target.value = ""; } // TODO : tags 배열에 새로운 태그를 추가하는 메소드를 완성하세요. // 이 메소드는 태그 추가 외에도 아래 3 가지 기능을 수행할 수 있어야 합니다. // - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기 // - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기 // - 태그가 추가되면 input 창 비우기 }; 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)} > { "x" /* TODO : tag-close-icon이 tag-title 오른쪽에 x 로 표시되도록 하고, 삭제 아이콘을 click 했을 때 removeTags 메소드가 실행되어야 합니다. */ } </span> </li> ))} </ul> <input className="tag-input" type="text" onKeyUp={(e) => { { if (e.key === "Enter") { addTags(e); } /* 키보드의 Enter 키에 의해 addTags 메소드가 실행되어야 합니다. */ } }} placeholder="Press enter to add tags" /> </TagsInput> </> ); };
-tags를 변경할때 push를 썼는데 작동되지않았다. 그래서 그냥 push는 쓰면 안되는건가 했는데... 다 이유가 있었다. 리액트는 참조형데이터의 경우, 주소값이 바뀌어야 변한것을 인지하기 때문이다.(!) 원시형데이터는 바뀌는대로 인지하니까 상관없다고 한다.
과제하면서 필요했던 지식들
useRef
- useRef는 .current 프로퍼티로 전달된 인자로 초기화된 변경 가능한 ref 객체를 반환한다.
- 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지된다.
- useRef는 .current 프로퍼티에 변경가능한 값을 담고있는 상자와 같다.
- .cureent 프로퍼티를 변경하더라도 리렌더링을 발생시키진 않는다.
onBlur
onBlur 이벤트는 엘리먼트(또는 자식엘리먼트)에서 포커스가 사라졌을때 호출된다. (인풋의 바깥영역을 클릭했을때라든가)
반대로 onFocus이벤트는 엘리먼트(또는 자식 엘리먼트)가 포커스 될때 호출된다.
'코드스테이츠 43기' 카테고리의 다른 글
[2022.2.28.] (0) 2023.02.28 [TIL] 2022.2.24.(금) 상태관리(리덕스) (0) 2023.02.24 [TIL] 2022.2.21.(화) (0) 2023.02.21 [TIL] 2022.2.20.(월) (0) 2023.02.20 [TIL /2023.02.17] UI/UX (0) 2023.02.17