모달
export const Modal = () => {
const [isOpen, setIsOpen] = useState(false);
const openModalHandler = () => {
// TODO : isOpen의 상태를 변경하는 메소드를 구현합니다.
setIsOpen(!isOpen)
};
return (
<>
<ModalContainer>
<ModalBtn onClick={openModalHandler}
// TODO : 클릭하면 Modal이 열린 상태(isOpen)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다.
>
{isOpen ? "Opened!" :"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={(event)=>event.stopPropagation()}>
<button onClick={openModalHandler}>x</button>
<div>HELLO CODESTATES!</div>
</ModalView>
</ModalBackdrop>:null}
</ModalContainer>
</>
);
};
기본적으로
버튼 onClick 이벤트 발생 => Modal 상태변경함수 실행
true 일때 <ModalBackdrop>컴포넌트 랜더링 false일땐 null
<ModalBackdrop> 은 모달창자체인 <ModalView>컴포넌트를 자식요소로 가지고있다.
<ModalBackdrop>를 클릭했을때
Modal의 상태변경함수를 실행시켜(false)
조건부 렌더링에서 null값이 나오므로 화면에서 사라진다.
토글
export const Toggle = () => {
const [isOn, setisOn] = useState(false);
const toggleHandler = () => {
// TODO : isOn의 상태를 변경하는 메소드를 구현합니다.
setisOn(!isOn)
};
return (
<>
<ToggleContainer onClick={toggleHandler}
// TODO : 클릭하면 토글이 켜진 상태(isOn)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다.
>
{/* TODO : 아래에 div 엘리먼트 2개가 있습니다. 각각의 클래스를 'toggle-container', 'toggle-circle' 로 지정하세요. */}
{/* TIP : Toggle Switch가 ON인 상태일 경우에만 toggle--checked 클래스를 div 엘리먼트 2개에 모두 추가합니다. 조건부 스타일링을 활용하세요. */}
<div className={`toggle-container ${isOn ? "toggle--checked" : ""}`} />
<div className={`toggle-circle ${isOn ? "toggle--checked" : ""}`}/>
</ToggleContainer>
<Desc>
{isOn ? "Toggle Switch ON" : 'Toggle Switch OFF'}
</Desc>
{/* TODO : Desc 컴포넌트를 활용해야 합니다. */}
{/* TIP: Toggle Switch가 ON인 상태일 경우에 Desc 컴포넌트 내부의 텍스트를 'Toggle Switch ON'으로, 그렇지 않은 경우 'Toggle Switch OFF'가 됩니다. 조건부 렌더링을 활용하세요. */}
</>
);
};
모달과 마찬가지로 클릭 이벤트를 감지해야한다.
토글 onClick 이벤트 발생 => 토글 상태변경함수 실행
2가지 <div>가 있다.
토글을 감싸는 <div>
토글안의 원형버튼 <div>
className을 클릭이 되었을때 동적으로 줘야하기 때문에
중괄호 안에 삼항연산자로 위에 onClick이벤트에 따라 적용될 수 있도록 만들어준다.
<Desc> 도 마찬가지로 isOn 상태에 따라 렌더링될 문자열을
삼항연산자로 만들어준다.
탭
export const Tab = () => {
// TIP: Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한
// currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0 입니다.
let [isClicked, setIsClicked] = 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) => {
// TIP: parameter로 현재 선택한 인덱스 값을 전달해야 하며, 이벤트 객체(event)는 쓰지 않습니다
// TODO : 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신되도록 함수를 완성하세요.
setIsClicked(isClicked = index)
};
return (
<>
<div>
<TabMenu>
{/*TODO: 아래 하드코딩된 내용 대신에, map을 이용한 반복으로 코드를 수정합니다.*/}
{/*TIP: li 엘리먼트의 class명의 경우 선택된 tab 은 'submenu focused' 가 되며,
나머지 2개의 tab은 'submenu' 가 됩니다.*/}
{menuArr.map((el)=>{
return(
<li key={menuArr.indexOf(el)} onClick = {()=>selectMenuHandler(menuArr.indexOf(el))} className={`${isClicked===menuArr.indexOf(el)?'submenu focused':'submenu'}`}>{el.name}</li>
)
})}
{/* <li className="submenu">{menuArr[0].name}</li>
<li className="submenu">{menuArr[1].name}</li>
<li className="submenu">{menuArr[2].name}</li> */}
</TabMenu>
<Desc>
{/*TODO: 아래 하드코딩된 내용 대신에, 현재 선택된 메뉴 따른 content를 표시하세요*/}
<div>
<div>
<p>{menuArr[isClicked].content}</p>
</div>
</div>
{/* <p>{menuArr[0].content}</p> */}
</Desc>
</div>
</>
);
};
여기서 map을 사용하는게 정말 헷갈렸었다.
3개의 탭은 배열형태로 되어 있으므로
각각의 인덱스 값을 클릭했을때, 렌더링에 사용할 key값에, className을 구분할 용도로 사용했다.
밑에 <p> 태그안에 content를 넣는것도 정말 헷갈렸었는데
처음엔 map 함수 써서 왜 3개 다 렌더링이 되지 ? 이러고 있었다 !
결론은
배열의 클릭된 인덱스의 context 값을 들고오면 된다.
당연하게보이지만 왜 문제 풀때는 안보이던지 ㅠㅠ
클릭된 요소의 인덱스는 isClicked에 저장되어있으므로
대괄호안에 변수를 넣어준다.
리액트에서 리스트를 map메소드로 렌더링할 때 'key' prop를 사용하는것이 좋다.
key는 각각의 리스트 아이템을 구분하는 방법으로
사용하지 않을시 리액트는 비효율적인 방법으로 리스트 아이템을 비교하게 된다.
key prop은 리액트의 가상 DOM에서 엘리먼트의 고유식별자로 사용되기 때문에
없다면 중복된 엘리먼트가 생성될 수 있어 의도치 않은 동작을 실행시킬 수 있다.
태그
export const Tag = () => {
const initialTags = ['CodeStates', 'kimcoding'];
const [tags, setTags] = useState(initialTags);
const removeTags = (indexToRemove) => {
// TODO : 태그를 삭제하는 메소드를 완성하세요.
// 배열의 특정값 삭제? splice쓰고 실패했다. 원본배열 .....건들지말자
//처음에 이렇게 적음
//setTags([...tags.slice(0, indexToRemove)], [...tags.slice(indexToRemove + 1)])
setTags([...tags.slice(0,indexToRemove),...tags.slice(indexToRemove+1)])
};
const addTags = (event) => {
// 엔터 이벤트 여기다 줘도 될거같다!!
// TODO : tags 배열에 새로운 태그를 추가하는 메소드를 완성하세요.
// 이 메소드는 태그 추가 외에도 아래 3 가지 기능을 수행할 수 있어야 합니다.
// - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기
// - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기
// - 태그가 추가되면 input 창 비우기
console.log(tags)
if(tags.includes(event.target.value)){
//아무것도 안할 이벤트는 비워두기
}else if(event.target.value === ""){
}else{
setTags([...tags,event.target.value])
event.target.value = ""
}
console.log(tags)
};
return (
<>
<TagsInput>
<ul id="tags">
{tags.map((tag, index) => (
<li key={index} className="tag">
<span className="tag-title">{tag}</span>
<span onClick={()=>removeTags(index)} className="tag-close-icon">
{/* TODO : tag-close-icon이 tag-title 오른쪽에 x 로 표시되도록 하고,
삭제 아이콘을 click 했을 때 removeTags 메소드가 실행되어야 합니다. */}
X
</span>
</li>
))}
</ul>
<input
className="tag-input"
type="text"
onKeyUp={(event)=>event.key === 'Enter'? addTags(event):null} //여기서 event중괄호썼었다 너무 헷갈려..
placeholder="Press enter to add tags"
/>
</TagsInput>
</>
);
};
특정한 onKeyup 이벤트를 주기 위해 고군분투했다...
사실 addTags에
if (event.key === "Enter")
이렇게 줘도 되었었는데!
addTags(event)에서 ()를 {}로 해두고 왜 안되는거징,,하고있었다.
괜히 더 복잡한것 같지만 그래도 하나 배웠다!
input에서 엔터키가 눌렸을 때
includes를 이용해서 중복값이 있는지
""빈 값은 아닌지
검사 후 태그 리스트 배열에 추가해준다.
여기서도 push를 썼다가 원본배열이 수정되는 바람에.... 스프레드문법으로 갈아탔다.
이렇게 추가 해준 후 input의 value를 빈 문자열로 초기화 시켜준다.
이미 있던 코드에 tags에 map 함수를 걸어서 두 개의 인자를 !!!! 주는 방법이 있었다
배열의 요소와 그것의 인덱스값을..!
앞에서 동작원리 실컷 공부해놓고 다까먹기 ..^^
삭제 기능은
또 인간은 같은 실수를 반복하고
splice로 특정 index의 값을 없애보려다가 망하고
tag 상태 변경함수에 인자로
[...tags.slice(처음부터 특정인덱스 앞까지), ...tags.slice(특정인덱스보다 하나 뒤부터 끝까지)]
이렇게 새로운 배열을 만들어주었다.
'React' 카테고리의 다른 글
[React] 쇼핑몰앱 장바구니기능을 구현해보자 (2) | 2023.04.21 |
---|---|
[React]검색어 자동완성과 클릭투에디트를 구현해보자 (1) | 2023.04.20 |
useEffect훅을 사용해 과제를 풀어보았다. (0) | 2023.04.03 |
[React] Effect Hook (0) | 2023.04.01 |
[React] 데이터 흐름과 state끌어올리기 (Lifting State Up) (2) | 2023.03.31 |